Splitting a Scala List into parts using a given list of part sizes.[Partitioning] - scala

I got two lists:
val list1:List[Int] = List(5, 2, 6)
val list2:List[Any] = List("a", "b", "c", "d", "e", "f", "g", "h", "i", "j","k")
such that list1.sum >= list2.size
I want a list of lists formed with elements in list2 consecutively
with the sizes mentioned in list1.
For example:
if list1 is List(5,2,4) the result I want is:
List(List("a", "b", "c", "d", "e"),List("f", "g"),List("h", "i", "j","k"))
if list1 is List(5,4,6) the result I want is:
List(List("a", "b", "c", "d", "e"),List("f", "g","h", "i"),List("j","k"))
How can I do that with concise code.

Turn list2 into an Iterator then map over list1.
val itr = list2.iterator
list1.map(itr.take(_).toList)
//res0: List[List[Any]] = List(List(a, b, c, d, e), List(f, g), List(h, i, j, k))
update:
While this appears to give the desired results, it has been pointed out elsewhere that reusing the iterator is actually unsafe and its behavior is not guaranteed.
With some modifications a safer version can be achieved.
val itr = list2.iterator
list1.map(List.fill(_)(if (itr.hasNext) Some(itr.next) else None).flatten)
-- or --
import util.Try
val itr = list2.iterator
list1.map(List.fill(_)(Try(itr.next).toOption).flatten)

you can slice on list2 based on the size you get from list1,
def main(args: Array[String]): Unit = {
val groupingList: List[Int] = List(5, 2, 4)
val data = List("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k")
//0, 5
//5, (5 + 2)
//5 + 2, (5 + 2 + 4)
val grouped = groupingList.zipWithIndex.map { case (_, index) =>
val start = groupingList.slice(0, index).sum
val end = groupingList.slice(0, index + 1).sum
data.slice(start, end)
}
println(grouped)
}
result: List(List(a, b, c, d, e), List(f, g), List(h, i, j, k))
Also read: How slice works

i like show it using ex
list1=List("one","two","three")
List[String] = List(one, two, three)
list2=List("red","green","blue")
List[String] = List(red, green, blue)
list1::list2
List[java.io.Serializable] = List(List(one, two, three), red, green, blue)
var listSum=list1::list2
List[java.io.Serializable] = List(List(one, two, three), red, green, blue)
listSum
List[java.io.Serializable] = List(List(one, two, three), red, green, blue)
we can "::" for insert one list to another list in Scala

Just found by me, even the following code worked, but the code posted by jwvh appears more concise than this, and I understand making list1 a Vector makes more sense regarding performance of element access in the following code:
list1.scan(0)(_+_).sliding(2).toList.map(x=>list2.drop(x(0)).take(x(1)-x(0)))
or
list1.scan(0)(_+_).zip(list1).map(x=>list2.drop(x._1).take(x._2))
list1.scan(0)(_+_).sliding(2).map(x=>list2.slice(x(0),x(1))).toList

Related

How to combine four lists into a list of lists in Scala [duplicate]

This question already has answers here:
How to perform transpose on List of Lists in scala?
(2 answers)
Closed 2 years ago.
Let's say I have 4 fixed lists, all the same length:
val list1: List[String] = List("a", "1", "w")
val list2: List[String] = List("b", "2", "x")
val list3: List[String] = List("c", "3", "y")
val list4: List[String] = List("d", "4", "z")
I want to have them in a combined/composite list that returns:
List(List("a","b","c","d"), List("1","2","3","4"), List("w","x","y","z"))
I have tried using fold and flatMaps but I can't seem to understand fully how to implement this. If someone can also explain a good solution as well so that I can learn, I will greatly appreciate it.
Use transpose:
val list1 = List("a", "1", "w")
val list2 = List("b", "2", "x")
val list3 = List("c", "3", "y")
val list4 = List("d", "4", "z")
List(list1, list2, list3, list4).transpose
// List(List(a, b, c, d), List(1, 2, 3, 4), List(w, x, y, z))
Updated answer, after reading updated question carefully :)
val list1: List[String] = List("a", "1", "w")
val list2: List[String] = List("b", "2", "x")
val list3: List[String] = List("c", "3", "y")
val list4: List[String] = List("d", "4", "z")
val result = List(list1, list2, list3, list4)
.flatMap(_.zipWithIndex)
.groupBy{ case(value,index) => index}
.values
.toList
.map(_.map(_._1))
Also in your code, those should be List[String]

Scala - How to group elements by occurrence?

Is there a function in scala that groups all elements of a list by the number of these occurrences?
For example, I have this list:
val x = List("c", "b", "b", "c", "a", "d", "c")
And I want to get a new list like that:
x = List((3, "c"), (2, "b"), (1, "a"), (1, "d"))
You can first count the occurrences of each element and then reverse the resulting tuples:
List("c", "b", "b", "c", "a", "d", "c")
.groupBy(identity).mapValues(_.size) // Map(b -> 2, d -> 1, a -> 1, c -> 3)
.toList // List((b,2), (d,1), (a,1), (c,3))
.map{ case (k, v) => (v, k) } // List((2,b), (1,d), (1,a), (3,c))
You don't specifically mention a notion of order for the output, but if this was a requirement, this solution would need to be adapted.
Try this to get exactly what you want in the order you mentioned. (ie., order preserved in the List while taking counts):
x.distinct.map(v=>(x.filter(_==v).size,v))
In SCALA REPL:
scala> val x = List("c", "b", "b", "c", "a", "d", "c")
x: List[String] = List(c, b, b, c, a, d, c)
scala> x.distinct.map(v=>(x.filter(_==v).size,v))
res225: List[(Int, String)] = List((3,c), (2,b), (1,a), (1,d))
scala>

Find the sum of a list in Scala

If I have a list of strings and I know the numeric vlaue of each string in the list how do i get the sum of the list?
Example:
I know:
a = 1
b = 2
c = 3
d = 4
e = 5
I am given the following list:
List("a","b","d")
what the best way of calculating the sum 7?
Thanks
val a = Map("a" -> 1, "b" -> 2, "c" -> 3, "d" -> 4, "e" -> 5)
val b = List("a", "b", "d")
b.map(a.getOrElse(_, 0)).sum
If you know that the values are the same as the element position, then you can avoid a map:
object test {
val list = List("a", "b", "c", "d", "e")
def sumThem = (for((letter, value) <- list.zipWithIndex) yield(value + 1)).sum
}
scala> test.sumThem
res2: Int = 15
If you're 100% sure it's only letters
List("a","b","d").foldLeft(0)(_ + _.hashCode - 96)
if not, you can map it before
val letters = (1 to 26).map(x => Character.toString((x+96).toChar) -> x).toMap
and use #sheunis's answer:
val input = List("a","b","d")
input.map(letters.getOrElse(_, 0)).sum

Scala collections: array groupBy and return array indexes for each group

I have an array, something like that:
val a = Array("a", "c", "c", "z", "c", "b", "a")
and I want to get a map with keys of all different values of this array and values with a collection of relevant indexes for each such group, i.e. for a given array the answer would be:
Map(
"a" -> Array(0, 6),
"b" -> Array(5),
"c" -> Array(1, 2, 4),
"z" -> Array(3)
)
Surprisingly, it proved to be somewhat more complicated that I've anticipated. The best I've came so far with is:
a.zipWithIndex.groupBy {
case(cnt, idx) => cnt
}.map {
case(cnt, arr) => (cnt, arr.map {
case(k, v) => v
}
}
which is not either concise or easy to understand. Any better ideas?
Your code can be rewritten as oneliner, but it looks ugly.
as.zipWithIndex.groupBy(_._1).mapValues(_.map(_._2))
Another way is to use mutable.MultiMap
import collection.mutable.{ HashMap, MultiMap, Set }
val as = Array("a", "c", "c", "z", "c", "b", "a")
val mm = new HashMap[String, Set[Int]] with MultiMap[String, Int]
and then just add every binding
as.zipWithIndex foreach (mm.addBinding _).tupled
//mm = Map(z -> Set(3), b -> Set(5), a -> Set(0, 6), c -> Set(1, 2, 4))
finally you can convert it mm.toMap if you want immutable version.
Here's a version with foldRight. I think it's reasonably clear.
val a = Array("a", "c", "c", "z", "c", "b", "a")
a
.zipWithIndex
.foldRight(Map[String, List[Int]]())
{case ((e,i), m)=> m updated (e, i::m.getOrElse(e, Nil))}
//> res0: scala.collection.immutable.Map[String,List[Int]] = Map(a -> List(0, 6)
//| , b -> List(5), c -> List(1, 2, 4), z -> List(3))
Another version using foldLeft and an immutable Map with default value:
val a = Array("a", "c", "c", "z", "c", "b", "a")
a.zipWithIndex.foldLeft(Map[String, List[Int]]().withDefaultValue(Nil))( (m, p) => m + ((p._1, p._2 +: m(p._1))))
// res6: scala.collection.immutable.Map[String,List[Int]] = Map(a -> List(6, 0), c -> List(4, 2, 1), z -> List(3), b -> List(5))
Starting in Scala 2.13, we can use the new groupMap which (as its name suggests) is a one-pass equivalent of a groupBy and a mapping over grouped items:
// val a = Array("a", "c", "c", "z", "c", "b", "a")
a.zipWithIndex.groupMap(_._1)(_._2)
// Map("z" -> Array(3), "b" -> Array(5), "a" -> Array(0, 6), "c" -> Array(1, 2, 4))
This:
zips each item with its index, giving (item, index) tuples
groups elements based on their first tuple part (_._1) (group part of groupMap)
maps grouped values to their second tuple part (_._2 i.e. their index) (map part of groupMap)

IndexedSeq and removing multiple elements

I have mySeq: IndexedSeq[A] and another myIncludedSeq: IndexedSeq[A], where every element of myIncludedSeq, is contained in mySeq.
I want to create a new IndexedSeq[A] from mySeq without all elements from myIncludedSeq.
I can't find any nice functional approach for this problem. How would you approach it?
Example:
val mySeq = IndexedSeq("a", "b", "a", "c", "d", "a")
val myIncludedSeq = IndexedSeq("a", "d", "a")
//magic
val expectedResult = IndexedSeq("b", "c", "a") //the order does not matter
How about this?
val original = IndexedSeq("a", "b", "a", "c", "d", "a")
val exclude = IndexedSeq("a", "d", "a")
val result = original.diff(exclude)
// IndexedSeq[String] = Vector(b, c, a)
From list's diff doc:
Computes the multiset difference between this list and another sequence.
Returns a new list which contains all elements of this list except
some of occurrences of elements that also appear in "excluding" list. If an
element value x appears n times in that, then the first n occurrences
of x will not form part of the result, but any following occurrences
will.