Combinations of Multiple Lists (Scala) - scala

I'm trying to write a function that takes multiple lists as input and returns a list of strings representation of every combination between those lists.
Sample Input :
val integers = List(1,2,3,4)
val characters = List('a','b','c')
val strings = List("apple","grapefruit")
Sample Output :
("1-a-apple", "1-a-grapefruit", .....)
Here is what I have so far :
def main(args: Array[String]): Unit = {
val integers = List(1,2,3,4)
val characters = List('a','b','c')
val strings = List("apple","grapefruit")
val lists = List(integers, characters, strings)
println(generator(lists).toString())
}
//using list api
def generator(x: List[List[Any]]): List[List[Any]] = x match {
case Nil => List(Nil)
case h :: _ => h.flatMap(i => generator(x.tail).map(i :: _))
}
And here is the output of my code :
List(List(1, a, apple), List(1, a, grapefruit), List(1, b, apple),
List(1, b, grapefruit), List(1, c, apple), List(1, c, grapefruit),
List(2, a, apple), List(2, a, grapefruit), List(2, b, apple), List(2,
b, grapefruit), List(2, c, apple), List(2, c, grapefruit), List(3, a,
apple), List(3, a, grapefruit), List(3, b, apple), List(3, b,
grapefruit), List(3, c, apple), List(3, c, grapefruit), List(4, a,
apple), List(4, a, grapefruit), List(4, b, apple), List(4, b,
grapefruit), List(4, c, apple), List(4, c, grapefruit))
So my questions are :
1)How can I get the output to be a list of strings instead of list of list of any?
2)How can I use a for comprehension instead of the list api?

val integers = List(1,2,3,4)
val characters = List('a','b','c')
val strings = List("apple","grapefruit")
for {
int <- integers
chars <- characters
string <- strings
} yield { s"${int}-${chars}-${string}" }
That code outputs
val res4: List[String] = List(1-a-apple, 1-a-grapefruit, 1-b-apple, 1-b-grapefruit, 1-c-apple, 1-c-grapefruit, 2-a-apple, 2-a-grapefruit, 2-b-apple, 2-b-grapefruit, 2-c-apple, 2-c-grapefruit, 3-a-apple, 3-a-grapefruit, 3-b-apple, 3-b-grapefruit, 3-c-apple, 3-c-grapefruit, 4-a-apple, 4-a-grapefruit, 4-b-apple, 4-b-grapefruit, 4-c-apple, 4-c-grapefruit)
Here's I'm using string interpolation (e.g. s" variable is $var1") to turn the items into strings.

Related

Scala - Dequeue doesn't return correct result

I have a queue of List[Any], but somehow dequeue doesn't return correct result and it doesn't remove the item from the queue either. Any idea why this happened?
Here is an example:
scala> val a = scala.collection.immutable.Queue(List(1, "A", "B", 987), List(2, "C", "D", 456), List(3, "E", "F", 123))
a: scala.collection.immutable.Queue[List[Any]] = Queue(List(1, A, B, 987), List(2, C, D, 456), List(3, E, F, 123))
scala> a.dequeue
res5: (List[Any], scala.collection.immutable.Queue[List[Any]]) = (List(1, A, B, 987),Queue(List(2, C, D, 456), List(3, E, F, 123)))
scala> a
res6: scala.collection.immutable.Queue[List[Any]] = Queue(List(1, A, B, 987), List(2, C, D, 456), List(3, E, F, 123))
You used an immutable queue, so dequeue does not modify the original queue but rather returns a modified one together with the head element as tuple:
val (elem, newQ) = oldQ.dequeue
For example, you can feed the tail queue into a recursive call for processing. Something like this:
def pq(q: Queue[List[Any]]): Unit = {
q.dequeue match {
case (head, tail) =>
println(head + "..."); // ... process head element ...
if (tail.nonEmpty) pq(tail)
}
}

How to join two ArrayList(List(String)] in parallel join

I have two ArrayList(List[String]) e.g:
ArrayList1: ArrayList1:
List(a, b, c) List(1, 2, 3)
List(d, e, f) List(4, 5, 6)
List(g,h, i) List(7,8, 9)
I want to use scala to join in parallel such as doing this
val ArrayList12 = ArrayList1 ++ ArrayList2
but this is not what I want since the ArrayList2 got added at the end of ArrayList1
Please I will appreciate help using scala to have a parallel join in this form:
List(a, b, c,1,2,3)
List(d, e, f,4,5,6)
List(g,h,i,7,8,9)
You can use zip.
val arrayList12 = arrayList1.zip(arrayList2).map(tuple => tuple._1 ++ tuple._2)
zip returns a List that combines each element of the first and the second list into a Tuple2, the resulting type is Seq[(List[A],List[Int])].
Then we can map that list to to a flat structure with map to concatenate the two lists in the Tuple.
Try this:
scala> List(List("a","b","c"), List("d", "e", "f"))
res1: List[List[String]] = List(List(a, b, c), List(d, e, f))
scala> List(List(1,2,3),List(4,5,6))
res2: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6))
scala> res1.zip(res2)
res3: List[(List[String], List[Int])] = List((List(a, b, c),List(1, 2, 3)), (List(d, e, f),List(4, 5, 6)))
which gives a List of tuples. You can then map() across these, adding the two elements of each tuple:
scala> res1.zip(res2).map(x => x._1 ++ x._2)
res5: List[List[Any]] = List(List(a, b, c, 1, 2, 3), List(d, e, f, 4, 5, 6))
Just another way of skinning the cat:
val arrayList1 = List(List("a","b","c"), List("d", "e", "f"), List("g","h", "i"))
val arrayList2 =List(List("1","2","3"), List("4", "5", "6"), List("7", "8", "9"))
List(arrayList1, arrayList2).transpose.map(_.flatten)
// List(List(a, b, c, 1, 2, 3), List(d, e, f, 4, 5, 6), List(g, h, i, 7, 8, 9))
But if I were doing it for real, I'd use Brian Agnew's answer

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))

Inserting at position in List

This insert function is taken from :
http://aperiodic.net/phil/scala/s-99/p21.scala
def insertAt[A](e: A, n: Int, ls: List[A]): List[A] = ls.splitAt(n) match {
case (pre, post) => pre ::: e :: post
}
I want to insert an element at every second element of a List so I use :
val sl = List("1", "2", "3", "4", "5") //> sl : List[String] = List(1, 2, 3, 4, 5)
insertAt("'a", 2, insertAt("'a", 4, sl)) //> res0: List[String] = List(1, 2, 'a, 3, 4, 'a, 5)
This is a very basic implementation, I want to use one of the functional constructs. I think I need
to use a foldLeft ?
Group the list into Lists of size 2, then combine those into lists separated by the separation character:
val sl = List("1","2","3","4","5") //> sl : List[String] = List(1, 2, 3, 4, 5)
val grouped = sl grouped(2) toList //> grouped : List[List[String]] = List(List(1, 2), List(3, 4), List(5))
val separatedList = grouped flatMap (_ :+ "a") //> separatedList : <error> = List(1, 2, a, 3, 4, a, 5, a)
Edit
Just saw that my solution has a trailing token that isn't in the question. To get rid of that do a length check:
val separatedList2 = grouped flatMap (l => if(l.length == 2) l :+ "a" else l)
//> separatedList2 : <error> = List(1, 2, a, 3, 4, a, 5)
You could also use sliding:
val sl = List("1", "2", "3", "4", "5")
def insertEvery(n:Int, el:String, sl:List[String]) =
sl.sliding(2, 2).foldRight(List.empty[String])( (xs, acc) => if(xs.length == n)xs:::el::acc else xs:::acc)
insertEvery(2,"x",sl) // res1: List[String] = List(1, 2, x, 3, 4, x, 5)
Forget about insertAt, use pure foldLeft:
def insertAtEvery[A](e: A, n: Int, ls: List[A]): List[A] =
ls.foldLeft[(Int, List[A])]((0, List.empty)) {
case ((pos, result), elem) =>
((pos + 1) % n, if (pos == n - 1) e :: elem :: result else elem :: result)
}._2.reverse
Recursion and pattern matching are functional constructs. Insert the new elem by pattern matching on the output of splitAt then recurse with the remaining input. Seems easier to read but I'm not satisfied with the type signature for this one.
def insertEvery(xs: List[Any], n: Int, elem: String):List[Any] = xs.splitAt(n) match {
case (xs, List()) => if(xs.size >= n) xs ++ elem else xs
case (xs, ys) => xs ++ elem ++ insertEvery(ys, n, elem)
}
Sample runs.
scala> val xs = List("1","2","3","4","5")
xs: List[String] = List(1, 2, 3, 4, 5)
scala> insertEvery(xs, 1, "a")
res1: List[Any] = List(1, a, 2, a, 3, a, 4, a, 5, a)
scala> insertEvery(xs, 2, "a")
res2: List[Any] = List(1, 2, a, 3, 4, a, 5)
scala> insertEvery(xs, 3, "a")
res3: List[Any] = List(1, 2, 3, a, 4, 5)
An implementation using recursion:
Note n must smaller than the size of List, or else an Exception would be raised.
scala> def insertAt[A](e: A, n: Int, ls: List[A]): List[A] = n match {
| case 0 => e :: ls
| case _ => ls.head :: insertAt(e, n-1, ls.tail)
| }
insertAt: [A](e: A, n: Int, ls: List[A])List[A]
scala> insertAt("'a", 2, List("1", "2", "3", "4"))
res0: List[String] = List(1, 2, 'a, 3, 4)
Consider indexing list positions with zipWithIndex, and so
sl.zipWithIndex.flatMap { case(v,i) => if (i % 2 == 0) List(v) else List(v,"a") }

create pairs from sets

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.