Reputation: 2182
Let's say there are three collections:
val numbers = List("1", "2")
val signs = List("-", "+")
val chars = List("a", "b")
I want to generate combinations of elements of those collections. What I want is not exactly a cartesian product, nor all possible combinations. What I want to have is something like this:
(1)
(1, -)
(1, -, a)
(1, -, b)
(1, +)
(1, +, a)
(1, +, b)
...
If I could sum this up in a set of formulas, I want to have these sets:
numbers
signs
chars
numbers * signs
numbers * chars
signs * chars
numbers * signs * chars
with important note that each of the product can contain only one element from each of the sets. These tuples, for example, would not be something I want in my result:
(1, 2, -)
(a, -, +)
because they have two numbers or two signs.
Any hints on how I could approach this interesting problem?
I think Python package itertools has product
function that deals with this, but I could not find anything similar for Scala.
Upvotes: 1
Views: 355
Reputation: 2182
Thanks everyone for hints, I managed to solve the issue. This is how I did it...
First I defined a set of collection names, let's call them that way:
val set: Set[String] = Set("numbers", "signs", "chars")
In addition to that, I defined their values:
val valueMap: Map[String, List[String]] = Map("numbers" -> List("1", "2", "3"), "signs" -> List("+", "-"), "chars" -> List("a", "b")
Then I did some mapping and magic:
val kpiComboPhase1 = set.subsets.toList.map(aSet => {
aSet.toList.flatMap(el => valueMap.get(el))
})
val kpiComboPhase2 = kpiComboPhase1.map(l => {
if (l.length === 1) l.flatten else l
})
This helped me to get something like this:
Set()
Set(numbers)
Set(signs)
Set(chars)
Set(numbers, signs)
Set(numbers, chars)
Set(signs, chars)
Set(numbers, signs, chars)
After that, I used values of each of those sets from valueMap
(each value is List[String]
and applied this approach https://stackoverflow.com/a/42095955/589571 to make a recursive cross product of arbitrary number of lists. I needed little more of mapping and gymnastics to get me the structure I wanted to have but in general, this was it.
Upvotes: 0
Reputation: 51271
One problem with your request is that tuples of different sizes are actually different types, so you don't want to mix them in a collection.
Using a List[List[String]]
to express the result, I think this gets at what you want.
val numbers = List("1", "2")
val signs = List("-", "+")
val chars = List("a", "b")
numbers.flatMap{n =>
List(n) :: signs.flatMap{s =>
List(s) :: List(n,s) :: chars.flatMap{c =>
List(List(c), List(n,c), List(s,c), List(n,s,c))
}
}
}.distinct
Upvotes: 0
Reputation: 2392
I guess what you want is all possible subsets of elements from those collections, in order and not repeating. You can do something alike:
val res: List[List[String]] = (for (x <- numbers) yield List(x)) ++
(for { x <- numbers; y <- signs } yield List(x,y)) ++
(for { x <- numbers; y <- signs; z <- chars } yield List(x, y, z))
Basically, it's a mixing of @jwvh and @Dima's answers. If you want to obtain tuples instead of lists, you can do:
res.map(s => s match {
case List(c) => (c)
case List(x, y) => (x, y)
case List(x,y,z) => (x,y,z)
case _ => (s)
})
The output:
scala> res.map(s => s match { case List(c) => (c); case List(x, y) =>
(x,y); case List(x,y,z) => (x,y,z); case _ => (s) })
res21: List[java.io.Serializable] = List(1, 2, (1,-), (1,+), (2,-), (2,+),
(1,-,a), (1,-,b), (1,+,a), (1,+,b), (2,-,a), (2,-,b), (2,+,a), (2,+,b))
Recall that this solution is very specific to your problem.
Upvotes: 1
Reputation: 40500
"Product" in scala would look something like this:
for {
n <- numbers
s <- signs
c <- chars
} yield (n, s, c)
That should help you get started hopefully.
Upvotes: 0