Looking at the source code, there's something useful tips for handling this:
In the binding there's code like this:
def bindFromRequest(data: Map[String, Seq[String]]): Form[T] = {
bind {
data.foldLeft(Map.empty[String, String]) {
case (s, (key, values)) if key.endsWith("[]") => s ++ values.zipWithIndex.map { case (v, i) => (key.dropRight(2) + "[" + i + "]") -> v }
case (s, (key, values)) => s + (key -> values.headOption.getOrElse(""))
}
}
}
Which indicates that no integer will mean they automatically get one added in. The seq mapping itself though has code like:
...
def indexes(key: String, data: Map[String, String]): Seq[Int] = {
val KeyPattern = ("^" + java.util.regex.Pattern.quote(key) + """\[(\d+)\].*$""").r
data.toSeq.collect { case (KeyPattern(index), _) => index.toInt }.sorted.distinct
}
...
//in the bind method for seq
...
val allErrorsOrItems: Seq[Either[Seq[FormError], T]] = RepeatedMapping.indexes(key, data).map(i => wrapped.withPrefix(key + "[" + i + "]").bind(data))
...
...
which indicates that if the form mapping's value between the [] is not a number, then it won't be pulled from the list. Testing this in the console shows this to be true:
import play.api.data._
import play.api.data.Forms._
val f = Form(single("s" -> seq(boolean)))
f.bind(Map("s[0]" -> "true", "s[1]" -> "false", "s[x]" -> "true")).get
// Seq[Boolean] = List(true, false) <-- we lose that last one from s[x]
But also it means the number doesn't actually matter:
f.bind(Map("s[1000]" -> "true", "s[2000]" -> "false", "s[320]" -> "true")).get
// Seq[Boolean] = List(true, true, false)
And lastly, to demostrate that a request being bound can just be handled by not putting numbers at all:
f.bindFromRequest(Map("s[]" -> Seq("true", "false","true"))).get
// Seq[Boolean] = List(true, false, true)