0

Consider such a map:

Map("one" -> Iterable(1,2,3,4), "two" -> Iterable(3,4,5), "three" -> Iterable(1,2))

I want to get a list of all possible permutations of elements under Iterable, one element for each key. For this example, this would be something like:

// first element of "one", first element of "two", first element of "three"
// second  element of "one", second element of "two", second element of "three"
// third  element of "one", third element of "two", first element of "three"
// etc.
Seq(Iterable(1,3,1), Iterable(2,4,2), Iterable(3,5,1),...)

What would be a good way to accomplish that?

George
  • 8,368
  • 12
  • 65
  • 106
  • 1
    That's not permutations. Permutations are single set in all possible orders, so like 1,2,3; 1,3,2; 2,1,3; 2,3,1; 3,1,2; 3,2,1. What you probably mean is "transposition" (elements by second index first and than first index). – Jan Hudec May 23 '11 at 13:14
  • What defines the order of keys, given that Map keys are otherwise not ordered? – The Archetypal Paul May 23 '11 at 13:36
  • @Paul I don't want it to be ordered. Consider `Iterable(1,2,3)`, `Iterable(2,3,1)`, `Iterable(3,1,2)` and `Iterable(3,2,1)` are the same. – George May 23 '11 at 13:58
  • So you want to treat the keys in the map as unordered, the values within each value iterator as unordered, the results as an unordered set of unordered values (one from each map value)? Just checking, because your example IS ordered (as it is a Seq) – The Archetypal Paul May 23 '11 at 14:25
  • Yes, that's right, I want everything unordered. – George May 23 '11 at 14:31
  • 1
    You want the [cartesian product](http://en.wikipedia.org/wiki/Cartesian_product). – Raphael May 23 '11 at 17:15
  • Also, do you want to explicitly construct the resulting collection or is a wrapper enough for you? – Raphael May 23 '11 at 17:16
  • @Raphael - The example given is not the Cartesian product (or if it is, it's a very, very strange way to enumerate it). – Rex Kerr May 23 '11 at 18:20
  • If not the Cartesian, what does he want? Specification is lacking (read: absent). I don't know what disturbs me more: that somebody would ask for help with so vague a "specification" or that people spit out code anyway. No offense to those who want to help, of course; but aren't you wasting some time for a guess? – Raphael May 23 '11 at 20:38

3 Answers3

4
val m = Map("one" -> Iterable(1,2,3,4), "two" -> Iterable(5,6,7), "three" -> Iterable(8,9))

If you want every combination:

for (a <- m("one"); b <- m("two"); c <- m("three")) yield Iterable(a,b,c)

If you want each iterable to march up together, but stop when the shortest is exhuasted:

(m("one"), m("two"), m("three")).zipped.map((a,b,c) => Iterable(a,b,c))

If you want each iterable to wrap around but stop when the longest one has been exhausted:

val maxlen = m.values.map(_.size).max
def icf[A](i: Iterable[A]) = Iterator.continually(i).flatMap(identity).take(maxlen).toList
(icf(m("one")), icf(m("two")), icf(m("three"))).zipped.map((a,b,c) => Iterable(a,b,c))

Edit: If you want arbitrary numbers of input lists, then you're best off with recursive functions. For Cartesian products:

def cart[A](iia: Iterable[Iterable[A]]): List[List[A]] = {
  if (iia.isEmpty) List()
  else {
    val h = iia.head
    val t = iia.tail
    if (t.isEmpty) h.map(a => List(a)).toList
    else h.toList.map(a => cart(t).map(x => a :: x)).flatten
  }
}

and to replace zipped you want something like:

def zipper[A](iia: Iterable[Iterable[A]]): List[List[A]] = {
  def zipp(iia: Iterable[Iterator[A]], part: List[List[A]] = Nil): List[List[A]] = {
    if (iia.isEmpty || !iia.forall(_.hasNext)) part
    else zipp(iia, iia.map(_.next).toList :: part)
  }
  zipp(iia.map(_.iterator))
}

You can try these out with cart(m.values), zipper(m.values), and zipper(m.values.map(icf)).

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
2

If you are out for an cartesian product, I have a solution for lists of lists of something.

xproduct (List (List (1, 2, 3, 4), List (3, 4, 5), List (1, 2)))    
res3: List[List[_]] = List(List(1, 3, 1), List(2, 3, 1), List(3, 3, 1), List(4, 3, 1), List(1, 3, 2), List(2, 3, 2), List(3, 3, 2), List(4, 3, 2), List(1, 4, 1), List(2, 4, 1), List(3, 4, 1), List(4, 4, 1), List(1, 4, 2), List(2, 4, 2), List(3, 4, 2), List(4, 4, 2), List(1, 5, 1), List(2, 5, 1), List(3, 5, 1), List(4, 5, 1), List(1, 5, 2), List(2, 5, 2), List(3, 5, 2), List(4, 5, 2))

Invoke it with Rex' m:

xproduct (List (m("one").toList, m("two").toList, m("three").toList)) 
Community
  • 1
  • 1
user unknown
  • 35,537
  • 11
  • 75
  • 121
1

Have a look at this answer. The question is about a fixed number of lists to combine, but some answers address the general case.

Community
  • 1
  • 1
ziggystar
  • 28,410
  • 9
  • 72
  • 124