Scala - groupBy map values to list - scala

I have the following input data
((A, 1, 4), (A, 2, 5), (A, 3, 6))
I would like to produce the following result
(A, (1, 2, 3), (4, 5, 6))
through grouping input by keys
What would be the correct way to do so in Scala?

((A, 1, 4), (A, 2, 5), (A, 3, 6))
In the case that this represents a List[(String, Int, Int)] type then try the following.
val l = List(("A", 1, 4), ("A", 2, 5), ("A", 3, 6), ("B", 1, 4), ("B", 2, 5), ("B", 3, 6))
l groupBy {_._1} map {
case (k, v) => (k, v map {
case (k, v1, v2) => (v1, v2)
} unzip)
}
This will result in a Map[String,(List[Int], List[Int])], i.e., a map with string keys mapped to tuples of two lists.
Map(A -> (List(1, 2, 3), List(4, 5, 6)), B -> (List(1, 2, 3), List(4, 5, 6)))

Try something like this:
def takeHeads[A](lists:List[List[A]]): (List[A], List[List[A]]) =
(lists.map(_.head), lists.map(_.tail))
def translate[A](lists:List[List[A]]): List[List[A]] =
if (lists.flatten.isEmpty) Nil else {
val t = takeHeads(lists)
t._1 :: translate(t._2)
}
yourValue.groupBy(_.head).mapValues(v => translate(v.map(_.tail)))
This produces a Map[Any,Any] when used on your value... but it should get you going in the right direction.

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

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 into List of Count and List of Element

Let's say I have Scala list like this:
val mylist = List(4,2,5,6,4,4,2,6,5,6,6,2,5,4,4)
How can I transform it into list of count and list of element? For example, I want to convert mylist into:
val count = List(3,5,3,4)
val elements = List(2,4,5,6)
Which means, in mylist, I have three occurrences of 2, five occurrences of 4, etc.
In procedural, this is easy as I can just make two empty lists (for count and elements) and fill them while doing iteration. However, I have no idea on how to achieve this in Scala.
Arguably a shortest version:
val elements = mylist.distinct
val count = elements map (e => mylist.count(_ == e))
Use .groupBy(identity) to create a Map regrouping elements with their occurences:
scala> val mylist = List(4,2,5,6,4,4,2,6,5,6,6,2,5,4,4)
mylist: List[Int] = List(4, 2, 5, 6, 4, 4, 2, 6, 5, 6, 6, 2, 5, 4, 4)
scala> mylist.groupBy(identity)
res0: scala.collection.immutable.Map[Int,List[Int]] = Map(2 -> List(2, 2, 2), 5 -> List(5, 5, 5), 4 -> List(4, 4, 4, 4, 4), 6 -> List(6, 6, 6, 6))
Then you can use .mapValues(_.length) to change the 'value' part of the map to the size of the list:
scala> mylist.groupBy(identity).mapValues(_.length)
res1: scala.collection.immutable.Map[Int,Int] = Map(2 -> 3, 5 -> 3, 4 -> 5, 6 -> 4)
If you want to get 2 lists out of this you can use .unzip, which returns a tuple, the first part being the keys (ie the elements), the second being the values (ie the number of instances of the element in the original list):
scala> val (elements, counts) = mylist.groupBy(identity).mapValues(_.length).unzip
elements: scala.collection.immutable.Iterable[Int] = List(2, 5, 4, 6)
counts: scala.collection.immutable.Iterable[Int] = List(3, 3, 5, 4)
One way would be to use groupBy and then check the size of each "group":
val withSizes = mylist.groupBy(identity).toList.map { case (v, l) => (v, l.size) }
val count = withSizes.map(_._2)
val elements = withSizes.map(_._1)
You can try like this as well alternative way of doing the same.
Step - 1
scala> val mylist = List(4,2,5,6,4,4,2,6,5,6,6,2,5,4,4)
mylist: List[Int] = List(4, 2, 5, 6, 4, 4, 2, 6, 5, 6, 6, 2, 5, 4, 4)
// Use groupBy { x => x } returns a "Map[Int, List[Int]]"
step - 2
scala> mylist.groupBy(x => (x))
res0: scala.collection.immutable.Map[Int,List[Int]] = Map(2 -> List(2, 2, 2), 5 -> List(5, 5, 5), 4 -> List(4, 4, 4, 4, 4), 6 -> List(6, 6, 6, 6))
step - 3
scala> mylist.groupBy(x => (x)).map{case(num,times) =>(num,times.size)}.toList
res1: List[(Int, Int)] = List((2,3), (5,3), (4,5), (6,4))
step -4 - sort by num
scala> mylist.groupBy(x => (x)).map{case(num,times) =>(num,times.size)}.toList.sortBy(_._1)
res2: List[(Int, Int)] = List((2,3), (4,5), (5,3), (6,4))
step -5 - unzip to beak into to list it return tuple
scala> mylist.groupBy(x => (x)).map{case(num,times) =>(num,times.size)}.toList.sortBy(_._1).unzip
res3: (List[Int], List[Int]) = (List(2, 4, 5, 6),List(3, 5, 3, 4))

Scala grouping list into list tuples with one shared element

What would be short functional way to split list
List(1, 2, 3, 4, 5) into List((1,2), (2, 3), (3, 4), (4, 5))
(assuming you don't care if you nested pairs are Lists and not Tuples)
Scala collections have a sliding window function:
# val lazyWindow = List(1, 2, 3, 4, 5).sliding(2)
lazyWindow: Iterator[List[Int]] = non-empty iterator
To realize the collection:
# lazyWindow.toList
res1: List[List[Int]] = List(List(1, 2), List(2, 3), List(3, 4), List(4, 5))
You can even do more "funcy" windows, like of length 3 but with step 2:
# List(1, 2, 3, 4, 5).sliding(3,2).toList
res2: List[List[Int]] = List(List(1, 2, 3), List(3, 4, 5))
You can zip the list with its tail:
val list = List(1, 2, 3, 4, 5)
// list: List[Int] = List(1, 2, 3, 4, 5)
list zip list.tail
// res6: List[(Int, Int)] = List((1,2), (2,3), (3,4), (4,5))
I have always been a big fan of pattern matching. So you could also do:
val list = List(1, 2, 3, 4, 5, 6)
def splitList(list: List[Int], result: List[(Int, Int)] = List()): List[(Int, Int)] = {
list match {
case Nil => result
case x :: Nil => result
case x1 :: x2 :: ls => splitList(x2 :: ls, result.:+(x1, x2))
}
}
splitList(list)
//List((1,2), (2,3), (3,4), (4,5), (5,6))

All possible permutations of values for such a map

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?
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)).
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))
Have a look at this answer. The question is about a fixed number of lists to combine, but some answers address the general case.