Reputation: 1417
I'm trying to come up with an "elegant" solution to iterate over two lists (pairs of values), and perform some tests on the resulting values.
Any ideas? Here's what I have so far, but I get "value filter is not a member of (List[Int], List[Int])," which surprises me I thought this would work. AND, I feel like there must be a much cleaner way to express this in Scala.
val accounts = random(count = 100, minimum = 1, maximum = GPDataTypes.integer._2)
val ids = random(count = 100, minimum = 1, maximum = GPDataTypes.integer._2)
for ((id, accountId) <- (ids, accounts)) {
val g = new GPGlimple(Some(id), Some(timestamp), accountId, false, false, 2)
println(g)
g.accountId mustEqual accountId
g.id mustEqual id
g.created.get must beLessThan(System.currentTimeMillis)
g.layers must beNone
g.version must be equalTo 2
}
Upvotes: 0
Views: 123
Reputation: 16324
The simplest solution for this is zip
:
(ids zip accounts)
The documentation for zip
says:
Returns a list formed from this list and another iterable collection by combining corresponding elements in pairs.
In other words, zip
will return a list of tuples.
The zipped
method could also work here:
(ids, accounts).zipped
You can find the zipped
source for 2-tuples here
. Note that this is made available through an enrichment of (T, U)
where T
is implicitly viewable as a TraversableLike
and U
is implicitly viewable as an IterableLike
. That method returns a ZippedTraversable2
, which is a minimal interface that encapsulates this sort of zipped return, and behaves more efficiently for large sequences by inhibiting the creation of intermediary collections. These are generally more performant because they use iterators
internally, as can be seen in the source.
Note that the returns here are of different types, which could affect downstream behavior. One important difference is that the normal combinator methods on ZippedTraversable2
are slightly different that those on a Traversable
of tuples. The methods on ZippedTraversable2
generally expect a function of 2 arguments, while those on a Traversable
of tuples will expect a function with a single argument that is a tuple. For example, you can check this in the REPL for the foreach
method:
val s1 = List(1, 2, 3)
val s2 = List('a', 'b', 'c')
(s1 -> s2).zipped.foreach _
// ((Int, Char) => Any) => Unit = <function1>
(s1 zip s2).foreach _
// (((Int, Char)) => Any) => Unit = <function1>
//Notice the extra parens here, signifying a method with a tuple argument
This difference means that you sometimes have to use a different syntax when using zip
and zipped
:
(s1 zip s2).map { x => x._1 + x._2 }
(s1, s2).zipped.map { x => x._1 + x._2 } //This won't work! The method shouldn't expect a tuple argument
//conversely
(s1, s2).zipped.map { (x, y) => x + y }
(s1 zip s2).map { (x, y) => x + y } //This won't work! The method shouldn't expect 2 arguments
//Added note: methods with 2 arguments can often use the more concise underscore notation:
(s1, s2).zipped.map { _ + _ }
Note that if you use the case
notation, you're covered either way:
//case works for both syntaxes
(s1, s2).zipped.map { case (x, y) => x + y } \
(s1 zip s2).map { case (x, y) => x + y }
This works since the compiler understands this notation for methods with either two arguments, or a single tuple argument, as explained in section 8.5 of the spec:
val f: (Int, Int) => Int = { case (a, b) => a + b }
val g: ((Int, Int)) => Int = { case (a, b) => a + b }
Upvotes: 2
Reputation: 53869
Use zip:
for ((id, accountId) <- ids.zip(accounts)) {
// ...
}
Upvotes: 1