Reputation: 841
I'd like to transform a list to another - for example:
val a = List(x,y,z)
val b = List(x1,y1,z1)
Desired output
List(List(x1,y,z),List(x,y1,z),List(x,y,z1),List(x1,y1,z),
List(x1,y,z1),List(x,y1,z1),List(x1,y1,z1))
The order is to be preserved - hence the built-in combinations function wouldn't be useful. Is there another concise way of doing this with scala?
Upvotes: 4
Views: 323
Reputation: 67290
So, if the order of the permutations is not relevant but only the fact that all appear, it can be solved. The number of permutations is pow(2, list-length)
or 1 << list-length
. Here is a version:
def mix[A](a: Seq[A], b: Seq[A]): IndexedSeq[Seq[A]] = {
val sz = a.size
require(sz == b.size, "Sequence lengths do not match")
(0 until 1 << sz).map { i =>
(a zip b).zipWithIndex.map { case ((ax, bx), j) =>
if ((i & 1 << j) == 0) ax else bx
}
}
}
val a = List("x0", "y0", "z0")
val b = List("x1", "y1", "z1")
mix(a, b)
The outer loop 0 until 1 << sz
iterates over all permutations, e.g. here with lists of length 3, it goes from 0 until 7. The inner loop zips the two input lists along with the running index and then flips between the a and b elements depending on the index. This is done using bit-wise comparison with the outer index.
i 1<<j & == 0
000 001 true (a)
000 010 true (a)
000 100 true (a)
001 001 false (b)
001 010 true (a)
001 100 true (a)
010 001 true (a)
010 010 false (b)
010 100 true (a)
011 001 false (b)
011 010 false (b)
011 100 true (a)
etc.
Upvotes: 1
Reputation: 24812
def permut[A](l: List[(A,A)]): List[List[A]] = l match {
case Nil => List(List()) // or Nil :: Nil
case (a,b) :: tail =>
val t = permut(tail)
t.map(a :: _) ::: t.map(b :: _)
}
val a = List("x0", "y0", "z0")
val b = List("x1", "y1", "z1")
scala> permut(a zip b)
res22: List[List[String]] = List(List(x0, y0, z0), List(x0, y0, z1), List(x0, y1, z0), List(x0, y1,
z1), List(x1 , y0, z0), List(x1, y0, z1), List(x1, y1, z0), List(x1, y1, z1))
Upvotes: 3
Reputation: 37435
You could use the built-in permutations of Set to calculate and use them as a "belongs to" mask to determine which list should contribute an element to the final combined list. Using this idea, a possible implementation should be something like this:
def xorZip[T](l1:List[T], l2:List[T]):List[List[T]] = {
def maskedUnzip[T](l:List[((T,T),Int)], mask:Int=>Boolean):List[T] = l.map{ case ((x,y),index) => if (mask(index)) x else y}
val l1zl2 = l1.zip(l2).zipWithIndex
val mask = (0 until l1zl2.size).toSet.subsets.toList
mask.map(mask => maskedUnzip(l1zl2, mask.contains(_)))
}
Scala REPL:
scala> val l1 = List("a","b","c")
l1: List[String] = List(a, b, c)
scala> val l2 = List("x","y","z")
l2: List[String] = List(x, y, z)
scala> xorZip(l1,l2)
res0: List[List[String]] = List(List(x, y, z), List(a, y, z), List(x, b, z), List(x, y, c), List(a, b, z), List(a, y, c), List(x, b, c), List(a, b, c))
Upvotes: 0