What is the most efficient way to iterate over two lists (of differing length) backwards in Scala.
So for two lists
List(a,b,c) and List(1,2)
the pairs would be
(c,2) and (b,1)
Note: I would rather not do a reverse of each list.
What is the most efficient way to iterate over two lists (of differing length) backwards in Scala.
So for two lists
List(a,b,c) and List(1,2)
the pairs would be
(c,2) and (b,1)
Note: I would rather not do a reverse of each list.
A simple way is :
List('a','b','c').reverse zip List(1,2).reverse
Reversing the list is O(n)
however, if you're worried about efficiency.
According to List
's scaladoc, using reverseIterator
might be more efficient. That way you don't creat a new list like with reverse
, but traverse it as you keep iterating. That'd be :
val it = list1.reverseIterator zip list2.reverseIterator //returns an Iterator you can force
it.toList // List((c,2), (b,1))
Using parallel collections,
def parRevZip (a: List[String], b: List[Int]) = {
val max = Math.max(a.size, b.size)
val n = Math.abs(a.size - b.size)
if (a.size > b.size)
(max to n+1 by -1).par.map { i => (a(i-1), b(i-n-1)) }
else
(max to n+1 by -1).par.map { i => (a(i-n-1), b(i-1)) }
}
Taking into account different index values for possibly different sized lists, this approach fetches and pairs the same number of elements starting from the end of each list.
Performance needs careful evaluation; for small lists, a plain reverse and zipping may prove much simpler and efficient; for large lists, on the contrary, this parallel approach may be of interest.
Code Refinement
def parRevZip[A,B] (a: List[A], b: List[B]) = {
val aSize = a.size
val bSize = b.size
val max = Math.max(aSize, bSize)
val n = Math.abs(aSize - bSize)
if (aSize > bSize)
(max-1 to n by -1).par.map { i => (a(i), b(i-n)) }
else
(max-1 to n by -1).par.map { i => (a(i-n), b(i)) }
}
Using non recursive collections
Convenient immutable collections here where the computation of size is O(1)
(or quasi-constant) (see Recursive collections in Scala such as List) include for instance Array.
Hence,
def parRevZip[A,B] (a: Array[A], b: Array[B])
which does not follow any further the requirement of processing lists.
I think you mean this:
val a = List(1, 2, 3)
val b = List(8, 9)
val result = a.reverse zip b.reverse
Here is my attempt at this problem. The original lists a
and b
are not duplicated. The operation is O(N)
due to List.size
:
object test extends App {
val a = List("a", "b", "c") //> a : List[String] = List(a, b, c)
val b = List(1, 2) //> b : List[Int] = List(1, 2)
val aSize = a.size //> aSize : Int = 3
val bSize = b.size //> bSize : Int = 2
// find which is smaller and which is bigger list
val (smaller, bigger) = if (aSize < bSize) (a, b) else (b, a)
//> smaller : List[Any] = List(1, 2)
//| bigger : List[Any] = List(a, b, c)
// skip the extra entries from head of bigger list
val truncated = bigger.drop(Math.abs(aSize - bSize))
//> truncated : List[Any] = List(b, c)
val result = if (a == smaller)
smaller.zip(truncated).reverse
else
truncated.zip(smaller).reverse //> result : List[(Any, Any)] = List((c,2), (b,1))
}