Multi-Assignment based on Collection

假如想象 提交于 2019-12-23 12:52:08

问题


Edit

originally the question was "Collection to Tuple" as I assumed I needed a tuple in order to do variable multi-assignment. It turns out that one can do variable multi-assignment directly on collections. Retitled the question accordingly.

Original Have a simple Seq[String] derived from a regex that I would like to convert to a Tuple.

What's the most direct way to do so?

I currently have:

val(clazz, date) = captures match {
  case x: Seq[String] => (x(0), x(1))
}

Which is ok, but my routing layer has a bunch of regex matched routes that I'll be doing val(a,b,c) multi-assignment on (the capture group is always known since the route is not processed if regex does not match). Would be nice to have a leaner solution than match { case.. => ..}

What's the shortest 1-liner to convert collections to tuples in Scala?


回答1:


This is not an answer to the question but might solve the problem in a different way.

You know you can match a xs: List[String] like so:

val a :: b :: c :: _ = xs 

This assigns the first three elements of the list to a,b,c? You can match other things like Seq in the declaration of a val just like inside a case statement. Be sure you take care of matching errors:

Catching MatchError at val initialisation with pattern matching in Scala?




回答2:


You can make it slightly nicer using |> operator from Scalaz.

scala> val captures = Vector("Hello", "World")
captures: scala.collection.immutable.Vector[java.lang.String] = Vector(Hello, World)

scala> val (a, b) = captures |> { x => (x(0), x(1)) }
a: java.lang.String = Hello
b: java.lang.String = World

If you don't want to use Scalaz, you can define |> yourself as shown below:

scala> class AW[A](a: A) {
     |   def |>[B](f: A => B): B = f(a)
     | }
defined class AW

scala> implicit def aW[A](a: A): AW[A] = new AW(a)
aW: [A](a: A)AW[A]

EDIT:

Or, something like @ziggystar's suggestion:

scala> val Vector(a, b) = captures
a: java.lang.String = Hello
b: java.lang.String = World

You can make it more concise as shown below:

scala> val S = Seq
S: scala.collection.Seq.type = scala.collection.Seq$@157e63a

scala> val S(a, b) = captures
a: java.lang.String = Hello
b: java.lang.String = World



回答3:


As proposed by @ziggystar in comments you can do something like:

val (clazz, date) = { val a::b::_ = capture; (a, b)} 

or

val (clazz, date) = (capture(0), capture(1)) 

If you verified the type of the list before it is OK, but take care of the length of the Seq because the code will run even if the list is of size 0 or 1.




回答4:


Your question is originally specifically about assigning the individual capturing groups in a regex, which already allow you to assign from them directly:

scala> val regex = """(\d*)-(\d*)-(\d*)""".r
regex: scala.util.matching.Regex = (\d*)-(\d*)-(\d*)

scala> val regex(a, b, c) = "29-1-2012"
d: String = 29
m: String = 1
y: String = 2012

obviously you can use these in a case as well:

scala> "29-1-2012" match { case regex(d, m, y) => (y, m, d) }
res16: (String, String, String) = (2012,1,29)

and then group these as required.




回答5:


Seqs to tuple

To perform multi-assignment from a Seq, what about the following?

val Seq(clazz, date) = captures

As you see, no need to restrict to Lists; this code will throw a MatchError if the length does not match (in your case, that's good because it means that you made a mistake). You can then add

(clazz, date)

to recreate the tuple.

Tuples from matches

However, Jed Wesley-Smith posted a solution which avoids this problem and solves the original question better. In particular, in your solution you have a Seq whose length is not specified, so if you make a mistake the compiler won't tell you; with tuples instead the compiler can help you (even if it can't check against the regexp).



来源:https://stackoverflow.com/questions/9045418/multi-assignment-based-on-collection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!