create pairs from sets - scala

If I have unknown number of Set[Int] (or List[Int]) as an input and want to combine
i don't know size of input List[Int] for which I need to produce these tuples as a final result, what's the best way to achieve this? My code looks like below.

Ok. Since combine(xs) yields a List[List[Any]] and you have a :: combine(xs) you just insert a into the the List of all combinations. You want to combine a with each element of the possible combinations. That lead me to this solution.
You can also generalize it to lists:List[List[T]] because when you combine from lists:List[List[Int]] you will get a List[List[Int]].
def combine[T](lists: List[List[T]]): List[List[T]] = lists match {
case Nil => lists
case x :: Nil => for(a <- x) yield List(a) //needed extra case because if comb(xs) is Nil in the for loop, it won't yield anything
case x :: xs => {
val comb = combine(xs) //since all further combinations are constant, you should keep it in a val
for{
a <- x
b <- comb
} yield a :: b
}
}
Tests:
val first = List(7, 3, 1)
val second = List(2, 8)
val third = List("a","b")
combine(List(first, second))
//yields List(List(7, 2), List(7, 8), List(3, 2), List(3, 8), List(1, 2), List(1, 8))
combine(List(first, second, third))
//yields List(List(7, 2, a), List(7, 2, b), List(7, 8, a), List(7, 8, b), List(3, 2, a), List(3, 2, b), List(3, 8, a), List(3, 8, b), List(1, 2, a), List(1, 2, b), List(1, 8, a), List(1, 8, b))
I think you can also generalize this to work with other collections than List, but then you can't use pattern-match this easily and you have to work via iterators.

Related

Merge two collections by interleaving values

How can I merge two lists / Seqs so it takes 1 element from list 1, then 1 element from list 2, and so on, instead of just appending list 2 at the end of list 1?
E.g
[1,2] + [3,4] = [1,3,2,4]
and not [1,2,3,4]
Any ideas? Most concat methods I've looked at seem to do to the latter and not the former.
Another way:
List(List(1,2), List(3,4)).transpose.flatten
So maybe your collections aren't always the same size. Using zip in that situation would create data loss.
def interleave[A](a :Seq[A], b :Seq[A]) :Seq[A] =
if (a.isEmpty) b else if (b.isEmpty) a
else a.head +: b.head +: interleave(a.tail, b.tail)
interleave(List(1, 2, 17, 27)
,Vector(3, 4)) //res0: Seq[Int] = List(1, 3, 2, 4, 17, 27)
You can do:
val l1 = List(1, 2)
val l2 = List(3, 4)
l1.zip(l2).flatMap { case (a, b) => List(a, b) }
Try
List(1,2)
.zip(List(3,4))
.flatMap(v => List(v._1, v._2))
which outputs
res0: List[Int] = List(1, 3, 2, 4)
Also consider the following implicit class
implicit class ListIntercalate[T](lhs: List[T]) {
def intercalate(rhs: List[T]): List[T] = lhs match {
case head :: tail => head :: (rhs.intercalate(tail))
case _ => rhs
}
}
List(1,2) intercalate List(3,4)
List(1,2,5,6,6,7,8,0) intercalate List(3,4)
which outputs
res2: List[Int] = List(1, 3, 2, 4)
res3: List[Int] = List(1, 3, 2, 4, 5, 6, 6, 7, 8, 0)

How to sum two neighbours in a list in scala

If you have one Integer list in Scala, and you want to iterate through it and sum every two neighbours with the same value and return this as a list, how would one do that ?
So for example:
List(4, 4, 2, 6) => List(8, 2, 6)
I'm completely new to Scala, but I can imagine that pattern match or map could be useful.
def sumSameNeighbours: List[Int] => List[Int] = {
ls match {
case l1::l2:ls => l1 == l2
}
}
This is what I can think of.
EDIT: How would I have to change the code in order to iterate from right to left instead from left to right?
So that f.e. it would be:
List(2, 2, 2, 6, 4) => List(2, 4, 6, 4)
instead of
List(2, 2, 2, 6, 4) => List(4, 2, 6, 4)
This is pretty close to your suggestion and seems basically to work:
import scala.annotation.tailrec
def sumSameNeighbors( ls : List[Int] ) : List[Int] = {
#tailrec
def walk( unsummed : List[Int], reverseAccum : List[Int] ) : List[Int] = {
unsummed match {
case a :: b :: rest if a == b => walk( rest, a + b :: reverseAccum )
case a :: rest => walk( rest, a :: reverseAccum )
case Nil => reverseAccum.reverse
}
}
walk( ls, Nil )
}
Note: Based on final OP's specifications clarification, this answer doesn't exactly fit the question requirements.
Here is a solution using List.grouped(2):
list.grouped(2).toList
.flatMap {
case List(a, b) if a == b => List(a + b)
case l => l
}
The idea is to group consecutive elements by pair. If the pair has the same elements, we return their sum to be flatMaped and otherwise both elements untouched.
List(4, 4, 2, 6) => List(8, 2, 6)
List(2, 4, 4, 2, 6) => List(2, 4, 4, 2, 6)
List(2) => List(2)
List(9, 4, 4, 4, 2, 6) => List(9, 4, 8, 2, 6)
Another way using foldRight, which I find a good default for this sort of traversing a collection while creating a new one:
list.foldRight(List.empty[Int]) {
case (x, y :: tail) if x == y => (x + y) :: tail
case (x, list) => x :: list
}
Output of List(2, 2, 2, 6, 4) is List(2, 4, 6, 4) as requested.
The main thing I wasn't clear on from your examples is what the output should be if summing creates new neighbours: should List(8, 4, 2, 2) turn into List(8, 4, 4) or List(16)? This produces the second.

What is Scalas Product.productIterator supposed to do?

Can someone tell me why I am getting different results when using Tuple2[List,List] and List[List] as my Product in the code below? Specifically I would like to know why the second value of the list of lists gets wrapped in another list?
scala> val a = List(1,2,3)
a: List[Int] = List(1, 2, 3)
scala> val b = List(4,5,6)
b: List[Int] = List(4, 5, 6)
scala> val c = List(a,b)
c: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6))
scala> c.productIterator.foreach( println(_) )
List(1, 2, 3)
List(List(4, 5, 6)) // <-- Note this
scala> val d = (a,b)
d: (List[Int], List[Int]) = (List(1, 2, 3),List(4, 5, 6))
scala> d.productIterator.foreach( println(_) )
List(1, 2, 3)
List(4, 5, 6) // <-- Compared to this
(I have read the (absolutely minimal) description of Scala's Product and the productIterator method on http://www.scala-lang.org/api/current/index.html#scala.Product )
Basically, Tuple means a product between all of its elements, but a non-empty List is a product between its head and tail.
This happens for List, because all case classes extend Product, and represent a product between all their elements similar to tuples. And non-empty List is defined as a case class, containing head and tail: final case class ::[B](override val head: B, private[scala] var tl: List[B]) extends List[B], which inherits the default implementation of Product by case class.
You can observe more of this behaviour with other Lists with 1 or more than 2 elements:
scala> List(a).productIterator.foreach(println)
List(1, 2, 3)
List()
scala> List(a, a).productIterator.foreach(println)
List(1, 2, 3)
List(List(1, 2, 3))
scala> List(a, a, a).productIterator.foreach(println)
List(1, 2, 3)
List(List(1, 2, 3), List(1, 2, 3))

For-comprehensions of dynamic depth in Scala

I am writing a code that needs to generate all combinations of integer sequences which are (element-wise) within bounds of two other integer sequences. The code will be probably more readable than the above explanation:
def combinations(startingCounts: List[Int], endingCounts: List[Int] ) = for(
a <- startingCounts(0) to endingCounts(0);
b <- startingCounts(1) to endingCounts(1);
c <- startingCounts(2) to endingCounts(2)
) yield List(a, b, c)
combinations(List(0,7,3), List(1,7,5))
//^returns Vector(List(0, 7, 3), List(0, 7, 4), List(0, 7, 5), List(1, 7, 3), List(1, 7, 4), List(1, 7, 5))
The above code works as expected, but it has two problems:
It only works correctly with lists of a certain length. This isn't really an issue with my use-case, but in general it is.
The code length is proportional to the list length I need to take care of. In my case the length is 6 and I have a for-comprehension with 6 generators.
My question is: what is the best way of implementing the same function in a way that it works with all "bound list" lenghts? By "best" I mean correct, simple enough, and preferably not (much) slower than the original.
How about this?
def combinations(startingCounts: List[Int], endingCounts: List[Int] ) : IndexedSeq[List[Int]] = {
if(startingCounts.isEmpty)
IndexedSeq(Nil)
else
for{
ns <- combinations(startingCounts.tail, endingCounts.tail)
n <- startingCounts.head to endingCounts.head
} yield
n :: ns
}
Here is my initial solution. It looks OK, but I wonder if it can be done better.
import scala.annotation.tailrec
type SLInt = IndexedSeq[List[Int]]
def combinations2(startingCounts: List[Int], endingCounts: List[Int] ): SLInt = {
#tailrec
def inner(acc: SLInt, startingCounts: List[Int], endingCounts: List[Int]): SLInt = {
(startingCounts, endingCounts) match {
case (sh :: st, eh :: et) if (sh <= eh) => {
val newAcc = for(
ls <- acc;
last <- (sh to eh)
) yield (last :: ls)
inner(newAcc, st, et)
}
case (Nil, Nil) => acc
case _ => throw new IllegalArgumentException()
}
}
inner(IndexedSeq(List()), startingCounts.reverse, endingCounts.reverse)
}
combinations2(List(0,7,3), List(1,7,5))
//res3: SLInt = Vector(List(0, 7, 3), List(1, 7, 3), List(0, 7, 4), List(1, 7, 4), List(0, 7, 5), List(1, 7, 5))
The order of the results is different, but that doesn't make a difference. I am performing the List.reverse to avoid using List append operation and to use prepend instead, which should be constant time.

Flattening of List[List[Int]] whilst retaining only selected members

There is a List[List[Int]] of prime factors for integers 2..12:
List(List(2), List(3), List(2, 2), List(5), List(3, 2),
List(7), List(2, 2, 2), List(3, 3), List(5, 2),
List(11), List(3, 2, 2))
It needs to be flattened so that the resulting data structure contains only the longest sequence (greatest power) of each prime:
List(2,2,2,3,3,5,7,11)
For example, leaving out all but the greatest power of two:
List(List(2), List(3), List(2, 2), List(5), List(3, 2),
List(7), List(2, 2, 2), List(3, 3), List(5, 2),
List(11), List(3, 2, 2))
Within the initial list sub-lists of primes are always sorted in the descending order.
Struggling to find an elegant, preferably ≤O(n) solution.
My solution is far from ideal:
xs.flatMap(l=> l.groupBy(x=>x)).map(x=>(x._1,x._2.length)).
groupBy(_._1).mapValues(_.maxBy(_._2)).values.
map(x=>List.fill(x._2) (x._1)).flatten
This is a fair bit shorter than what you have; it's close enough conceptually that I expect you can figure it out:
xs.flatMap(_.groupBy(x=>x)).groupBy(_._1).
flatMap(_._2.sortBy(- _._2.length).head._2).toSeq.sorted
After some analysis the problem boils down to a simple merge of two sorted lists, but with a slight twist - it must add duplicate elements only once:
merge(List(5,3,3,2),List(7,5,3,2,2)
Must produce:
List(7,5,3,3,2,2)
Once there is such wonderful merge function the list of lists can be simply reduced from left to right.
Solution
def merge (xs:List[Int],ys:List[Int]):List[Int] = (xs,ys) match{
case (Nil,_) => ys
case (_,Nil) => xs
case (x::xxs,y::yys) => if (x==y) x::merge(xxs,yys)
else if (x>y) x::merge(xxs,ys)
else y::merge(xs,yys)
}
// note the simplicity of application
ll reduce merge
Tail recursive version of merge - avoiding stack overflow on long lists :
def merge (xs:List[Int],ys:List[Int]) = {
def m (rs:List[Int],xs:List[Int],ys:List[Int]):List[Int] = (xs,ys) match {
case (Nil,_) => ys reverse_:::rs
case (_,Nil) => xs reverse_:::rs
case (x::xxs,y::yys) => if (x==y) m(x::rs,xxs,yys)
else if (x>y) m(x::rs,xxs,ys)
else m(y::rs,xs,yys)
}
m(Nil,xs,ys).reverse
}
Faster imperative version of merge:
def merge (x:List[Int],y:List[Int]) = {
var rs = new scala.collection.mutable.ListBuffer[Int]()
var xs = x
var ys = y
while(!xs.isEmpty && !ys.isEmpty) {
if (xs.head>ys.head) {
rs+=xs.head
xs=xs.tail
} else if(xs.head==ys.head) {
rs+=xs.head
xs=xs.tail
ys=ys.tail
} else {
rs+=ys.head
ys=ys.tail
}
}
rs ++= xs ++= ys toList
}
val ll = List(List(2), List(3), List(2, 2), List(5), List(3, 2),
List(7), List(2, 2, 2), List(3, 3), List(5, 2),
List(11), List(3, 2, 2))
val s = ll.flatten toSet
s.map (n => ll.map (l => (n, l.count (_ == n)))).map (l => (l(0) /: l.tail) ((a, b) => if (a._2 > b._2) a else b))
produces:
scala.collection.immutable.Set[(Int, Int)] = Set((7,1), (11,1), (2,3), (5,1), (3,2))
expanding the factors and sorting them, to generate List (2,2,2,3,3,5,7,11) is left as an exercise.