If I have this:
val a = Array(\"a \",\"b \",\"c \")
val b = Array(\"x\",\"y\")
I would like to know if such a method exists which would let
I'm using the following extensively in my code. Note that this is working for an arbitrary number of lists. It is creating an Iterator instead of a collection, so you don't have to store the potentially huge result in memory.
Any improvements are very welcome.
/**
* An iterator, that traverses every combination of objects in a List of Lists.
* The first Iterable will be incremented fastest. So consider the head as
* the "least significant" bit when counting.*/
class CombinationIterator[A](val components: List[Iterable[A]]) extends Iterator[List[A]]{
private var state: List[BufferedIterator[A]] = components.map(_.iterator.buffered)
private var depleted = state.exists(_.isEmpty)
override def next(): List[A] = {
//this function assumes, that every iterator is non-empty
def advance(s: List[(BufferedIterator[A],Iterable[A])]): List[(BufferedIterator[A],A)] = {
if( s.isEmpty ){
depleted = true
Nil
}
else {
assert(!s.head._1.isEmpty)
//advance and return identity
val it = s.head._1
val next = it.next()
if( it.hasNext){
//we have simply incremented the head, so copy the rest
(it,next) :: s.tail.map(t => (t._1,t._1.head))
} else {
//we have depleted the head iterator, reset it and increment the rest
(s.head._2.iterator.buffered,next) :: advance(s.tail)
}
}
}
//zipping the iterables to the iterators is needed for resseting them
val (newState, result) = advance(state.zip(components)).unzip
//update state
state = newState
result
}
override def hasNext = !depleted
}
So using this one, you have to write new CombinationIterator(List(a,b))
to obtain an iterator that goes through every combination.
Note that the following version is not optimal (performance wise):
.
scala> def combination(xx: List[List[_]], i: Int): List[_] = xx match {
| case Nil => Nil
| case x :: xs => x(i % x.length) :: combination(xs, i/x.length)
| }
combination: (xx: List[List[_]], i: Int)List[_]
scala> def combinationIterator(ll: List[List[_]]): Iterator[List[_]] = {
| Iterator.from(0).takeWhile(n => n < ll.map(_.length).product).map(combination(ll,_))
| }
combinationIterator: (ll: List[List[_]])Iterator[List[_]]
scala> List(List(1,2,3),List("a","b"),List(0.1,0.2,0.3))
res0: List[List[Any]] = List(List(1, 2, 3), List(a, b), List(0.1, 0.2, 0.3))
scala> combinationIterator(res0)
res1: Iterator[List[_]] = non-empty iterator
scala> res1.mkString("\n")
res2: String =
List(1, a, 0.1)
List(2, a, 0.1)
List(3, a, 0.1)
List(1, b, 0.1)
List(2, b, 0.1)
List(3, b, 0.1)
List(1, a, 0.2)
List(2, a, 0.2)
List(3, a, 0.2)
List(1, b, 0.2)
List(2, b, 0.2)
List(3, b, 0.2)
List(1, a, 0.3)
List(2, a, 0.3)
List(3, a, 0.3)
List(1, b, 0.3)
List(2, b, 0.3)
List(3, b, 0.3)