All combinations of arbitrary number of sets using for comprehension - scala

Let's look at following source of code:
def foo(s1: Set[Int], s2: Set[Int], s3: Set[Int]): Set[Set[Int]] = {
for {
ss1 <- s1
ss2 <- s2
ss3 <- s3
} yield Set(ss1, ss2, ss3)
}
How to define analogous function for def foo(ss: Set[Int]*) ?

It's almost the same as the usual cartesian product, except that you have to cram all the results into sets instead of collecting them in ordered tuples:
/** Forms cartesian product of sets,
* then collapses each resulting tuple into a set.
*/
def collapsedCartesian[A](sets: Set[A]*): Set[Set[A]] = sets match
case Seq() => Set(Set.empty)
case Seq(h, t # _*) => for a <- h; b <- collapsedCartesian(t: _*) yield (b + a)
Note that here, the + adds an element to a set: set + elem, which is an oddly asymmetric operation to be denoted by such a symmetric symbol.
The outcome seems reasonably irregular:
collapsedCartesian(Set(1, 2), Set(3, 4)).foreach(println)
println("---")
collapsedCartesian(Set(1, 2), Set(1, 2)).foreach(println)
println("---")
collapsedCartesian(Set(1, 2, 3), Set(4, 5), Set(6, 7)).foreach(println)
println("---")
collapsedCartesian(Set(1, 2, 3), Set(2, 3, 4), Set(4, 5)).foreach(println)
gives:
Set(3, 1)
Set(4, 1)
Set(3, 2)
Set(4, 2)
---
Set(1)
Set(2, 1)
Set(2)
---
Set(7, 5, 1)
Set(6, 4, 2)
Set(6, 4, 1)
Set(7, 4, 1)
Set(6, 5, 1)
Set(7, 5, 3)
Set(7, 4, 2)
Set(6, 5, 2)
Set(6, 4, 3)
Set(7, 5, 2)
Set(7, 4, 3)
Set(6, 5, 3)
---
Set(5, 3, 1)
Set(5, 4, 2)
Set(5, 4, 1)
Set(4, 2)
Set(4, 1)
Set(5, 3)
Set(5, 3, 2)
Set(5, 4, 3)
Set(4, 2, 1)
Set(5, 2, 1)
Set(4, 3, 1)
Set(4, 3, 2)
Set(5, 2)
Set(4, 3)
Please don't ask how to do it in Spark, this exponentially exploding stuff is obviously useless for any dataset with more than just a couple of entries.

Related

Scala's List unpacking

So my problem is simple. I have come across this many times, and my brain is unable to find a solution.
How can I unpack a list into another list, for an indefinite amount of variables?
Here is what I mean.
val list1 = List(List(7, 4), List(7, 6))
val list2 = List(List(1), List(5), List(8))
val desired_list1 = List(List(1, 7, 4), List(5, 7, 4), List(8, 7, 4))
val desired_list2 = List(List(1, 7, 6), List(5, 7, 6), List(8, 7, 6))
//** The desired_list1 and 2 must be a List[List[Int]] it cannot be List[List[Any]]
//Here's my attempt, which oddly enough completely ignores all elements of list1(0) which are not the first(7).
val attempt = list2.map(i => i +: list1(0)).map(j => j.collect{ case k:Int => k; case l # a :: b => a}).map(m => m.map{ case i:Any => i.toString.toInt})
//The result is
attempt: List[List[Int]] = List(List(1, 7), List(5, 7), List(8, 7))
//while it should be:
val desired_list1 = List(List(1, 7, 4), List(5, 7, 4), List(8, 7, 4))
I need a way to unpack that is not manual, please do not tell me to do this:
val attempt = list2.map(k => k +: list1(0)).map{ case (k, List(x, y)) => (k, x, y)}
Basically, list1 could have any number of elements. e.g
val list1 = List(List(99, 83, 2, 3, 4), List(99, 83, 2, 5 7))
However, these numbers are never repeated, so I guess it could be a set as well. But I don't know much about sets or if it'd help in any way.
This seems to be what you want:
val lists: List[List[Int]] = List(List(7, 4), List(7, 6))
val prefixes: List[List[Int]] = List(List(1), List(5), List(8))
val res: List[List[Int]] = for{
prefix <- prefixes.flatten
rest <- lists
} yield prefix :: res
// res: List[List[Int]] = List(List(1, 7, 4), List(1, 7, 6), List(5, 7, 4), List(5, 7, 6), List(8, 7, 4), List(8, 7, 6))
Unless, you actually want a list that contains your desired_list1 and desired_list2 together, in which case you need:
val res3 = lists.map{ rest =>
prefixes.flatten.map{prefix =>
prefix :: rest
}
}
/// res3: List[List[List[Int]]] =
// List(
// List(
// List(1, 7, 4), List(5, 7, 4), List(8, 7, 4)
// ),
// List(
// List(1, 7, 6), List(5, 7, 6), List(8, 7, 6)
// )
// )
I am not sure exactly why you would want that but try this out:
val list1 = List(List(7, 4), List(7, 6))
val list2 = List(1, 5, 8)
list2.flatMap(element=> list1.map(innerList=> element:: innerList))
output: List[List[Int]] = List(List(1, 7, 4), List(1, 7, 6), List(5, 7, 4), List(5, 7, 6), List(8, 7, 4), List(8, 7, 6))

getting list by the first element in the list of lists

Hi I am new to scala and getting silly doubts, I have a list of lists which looks like this
(4,List(List(2, 4, 0, 2, 4), List(3, 4, 0, 2, 4), List(4, 0, 1, 2, 4)))
I want to get the lists which starts with 4. How to do it.
you use filter to traverse through the List and apply your predicate on each list to check if first elem is 4.
example:
scala> val (data, options) = (4, List(List(2, 4, 0, 2, 4), List(3, 4, 0, 2, 4), List(4, 0, 1, 2, 4)))
data: Int = 4
options: List[List[Int]] = List(List(2, 4, 0, 2, 4), List(3, 4, 0, 2, 4), List(4, 0, 1, 2, 4))
scala> options.filter(_.headOption.contains(data))
res0: List[List[Int]] = List(List(4, 0, 1, 2, 4))
Also see: Scala List.filter with two conditions, applied only once
There are several ways.
Here is another
listOfLists.collect{ case l # 4 :: _ => l}
Potentially more powerful because we can filter on the first n elements, e.g.
listOfLists.collect{ case l # 4 :: 0 :: 1 :: _ => l}
If you have a Tuple (Int, List[List[Int]]), and want to return Lists that start with the Int provided in the start, for this case 4:
I would recommend you do something like this:
val myTuple = (4,List(List(2, 4, 0, 2, 4), List(3, 4, 0, 2, 4), List(4, 0, 1, 2, 4)))
myTuple._2.filter(_.headOption.contains(myTuple._1))
And this will return List(List(4, 0, 1, 2, 4))
What we are doing here is, we are first accessing the List[List[Int]] in the Tuple by doing myTuple._2 then we filter to remove Lists that don't have a head value as 4 - which we passed in as myTuple._1.
Note we are using headOption instead of head to get the first element in a List, this is to handle exceptions where no List contains the value provided in the start, for this case 4 (more details on this can be found here http://www.bks2.com/blog/2012/12/31/head_vs_headOption/)
val t = (4, List(List(2, 4, 0, 2, 4), List(3, 4, 0, 2, 4), List(4, 0, 1, 2, 4)))
t._2.filter(_.head==t._1)
In REPL:
scala> t._2.filter(_.head==t._1)
res5: List[List[Int]] = List(List(4, 0, 1, 2, 4))

How to zip but not in pair?

Let a
Seq( Seq(1, 2, 5) , Seq(3, 4, 5) , Seq(2, 1, 0) )
I'd want to get :
Seq( Seq(1, 3, 2) , Seq(2, 4, 1) , Seq(5, 5, 0) )
For the moment I wrote this :
points.reduce((point_1, point_2) => point_1.zip(point_2))
With : points the Seq[Seq[Double]] and point_1 and point_2 the Seq[Double].
It returns an error because Scala's interpreter seems to try to make a pair with a Seq[(Double, Double)] and Double (I think). In my example, it tries to make a pair with Seq( (1, 3) ) and 2. I can be wrong but it's my interpretation of the problem for now.
Well, how to solve this bug ? I feel like I need to use flatten, no ?
The standard library can do that for you.
Seq( Seq(1, 2, 5) , Seq(3, 4, 5) , Seq(2, 1, 0) ).transpose
//res0: Seq[Seq[Int]] = List(List(1, 3, 2), List(2, 4, 1), List(5, 5, 0))
update
If you're intent on doing it yourself, in a functional fashion, here's one way.
val ssi: Seq[Seq[Int]] = Seq(Seq(1, 2, 5), Seq(3, 4, 5), Seq(2, 1, 0))
ssi.zipWithIndex.map{case (s,x) =>
s.indices.map(ssi(_)(x))
}
If you want to do it using map and fold,
val seqInput = Seq( Seq(1, 2, 5) , Seq(3, 4, 5) , Seq(2, 1, 0) )
// seqInput: Seq[Seq[Int]] = List(List(1, 2, 5), List(3, 4, 5), List(2, 1, 0))
val seqOutput = seqInput
.map(s => Seq(s))
.fold(
seqInput.head.map(_ => Seq.empty[Int])
)({
case (acc, seq) => acc.zipWithIndex.map({ case (accSeq, i) => accSeq :+ seq.head(i) })
})
// seqOutput: Seq[Seq[Int]] = List(List(1, 3, 2), List(2, 4, 1), List(5, 5, 0))

Scala: List of vectors to Unique List of items in immutable way

I am receiving list of vectors from some function like
(List(Vector(1), Vector(1, 2), Vector(1, 3), Vector(1, 2, 4), Vector(1, 5)))
I want to convert it into distinct values of integers like
List(1,2,3,4,5)
in Scala using complete immutability.
Please suggest what are the efficient ways to achieve it.
You can use the flatten and distinct methods on List.
val list = List(Vector(1),
Vector(1, 2),
Vector(1, 3),
Vector(1, 2, 4),
Vector(1, 5))
val flattened = list.flatten // Gives List(1, 1, 2, 1, 3, 1, 2, 4, 1, 5)
val distinct = flattened.distinct // Gives List(1, 2, 3, 4, 5)
Here is alternate solution.
val lst = (List(Vector(1), Vector(1, 2), Vector(1, 3), Vector(1, 2, 4), Vector(1, 5)))
lst.flatten.toSet.toList
List[Int] = List(5, 1, 2, 3, 4)

Scala: Any predefined function to iterate over list(0), then list(0, 1), then list(0, 1, 2), etc.?

I want to iterate over a scala list in an incremental way, i.e. the first pass should yield the head, the second the first 2 elements, the next the first 3, etc...
I can code this myself as a recursive function, but does a pre-existing function exist for this in the standard library?
You can use the .inits method to get there, albeit there may be performance issues for a large list (I haven't played around with making this lazy):
scala> val data = List(0,1,2,3,4)
data: List[Int] = List(0, 1, 2, 3, 4)
scala> data.inits.toList.reverse.flatten
res2: List[Int] = List(0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4)
You can use the take like so:
scala> val myList = 1 to 10 toList
myList: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> for(cnt <- myList.indices) yield myList.take(cnt+1)
res1: scala.collection.immutable.IndexedSeq[List[Int]] = Vector(List(1), List(1, 2), List(1, 2, 3), List(1, 2, 3, 4), List(1, 2, 3, 4, 5), List(1, 2, 3, 4, 5, 6), List(1, 2, 3, 4, 5, 6, 7), List(1, 2, 3, 4, 5, 6, 7, 8), List(1, 2, 3, 4, 5, 6, 7, 8, 9), List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
OK, since I've whined enough, here's an iterator version that tries reasonably hard to not waste space or compute more than is needed at at one point:
class stini[A](xs: List[A]) extends Iterator[List[A]] {
var ys: List[A] = Nil
var remaining = xs
def hasNext = remaining.nonEmpty
def next = {
val e = remaining.head
remaining = remaining.tail
ys = e :: ys
ys.reverse
}
}
val it = new stini(List(1, 2, 3, 4))
it.toList
//> List[List[Int]] =
// List(List(1), List(1, 2), List(1, 2, 3), List(1, 2, 3, 4))
Try: for((x, i) <- l.view.zipWithIndex) println(l.take(i + 1))
if you need something side-effected (I just did println to give you an example)