I have a problem where I need to make a tuplet of three elements. Let's suppose that I have a list, and I managed to write tuplet of two elements:
val list = (1 to 10).toList
val map1 = list.foldLeft(Map.empty[Int,String])( (map, value) => map + (value -> value.toString) )
Map(5 -> 5, 10 -> 10, 1 -> 1, 6 -> 6, 9 -> 9, 2 -> 2, 7 -> 7, 3 -> 3, 8 -> 8, 4 -> 4)
I want to make a tuplet of three elements. How can I do that?
I tried this code:
val map1 = list.foldLeft(Map.empty[Int,String])( (map, value, s) => map + (value -> value.toString -> value.toString) )
Map(5 -> 5 -> 5, 10 -> 10-> 10, 1 -> 1-> 1, 6 -> 6-> 6, 9 -> 9-> 9, 2 -> 2-> 2, 7 -> 7-> 7, 3 -> 3-> 3, 8 -> 8-> 8, 4 -> 4-> 4)
-> is just a sugar notation for a pair (a tuple of two items). The universal notation for tuples of any arity is a comma-delimited list in braces. E.g. (1,2,3) is a tuple of three integers, while as in your example the expression 1 -> 2 -> 3 would desugar to ((1,2),3), which is a tuple of a tuple of two ints and an int.
What you're trying to achieve with your code simply doesn't make any sense. A Map can be constructed from a list of pairs, treating the first element of the tuple as a key and the second as a value. Tuples of any other arities are not supported and wouldn't make sense in that case. You can however construct collections of other types (e.g., a List) containing tuples of any arities.
In general to convert a range into a Tuple3 you could do something like this:
(0 to 10) map (x=>(x,x*2,x+10))
res0: scala.collection.immutable.IndexedSeq[(Int, Int, Int)] = Vector((0,0,10), (1,2,11), (2,4,12), (3,6,13), (4,8,14), (5,10,15), (6,12,16), (7,14,17), (8,16,18), (9,18,19), (10,20,20))
To join 2 Seqs as a Tuple2 you zip them:
(1 to 5) zip (10 to 15)
res3: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,10), (2,11), (3,12), (4,13), (5,14))
scala has built in support for zipping up to arity 3:
((0 to 3),(4 to 6),(7 to 9)).zipped.toList
res6: List[(Int, Int, Int)] = List((0,4,7), (1,5,8), (2,6,9))
If you need to do something similar to higher arities there's product-collections:
(0 to 3) flatZip (4 to 6) flatZip (7 to 9) flatZip (10 to 12)
res7: org.catch22.collections.immutable.CollSeq4[Int,Int,Int,Int] =
CollSeq((0,4,7,10),
(1,5,8,11),
(2,6,9,12))
And finally there's shapeless which does lots of cool things but has a moderate learning curve.
Related
This question already has answers here:
Count occurrences of each element in a List[List[T]] in Scala
(3 answers)
Closed 4 years ago.
So I have a list such as this:
val list = List(List(1,2,3),List(3,4,8),List(4,2,3))
In the above case, the output I want looks like this:
Map(1 -> 1, 2 -> 1, 3 -> 3, 4 -> 2, 8 -> 1)
If the List was a normal list, I would do:
list.groupBy(identity).mapValues(_.size)
To achieve the expected outcome. So, how do I do this for a 2d list?
Using only scala, you can flatten the first list and then apply the same method
list.flatten.groupBy(identity).mapValues(_.size) // Map(1 -> 1, 2 -> 2, 3 -> 3, 8 -> 1, 4 -> 2)
Or using Cats/Scalaz |+| (combine) operator.
list.map(l => l.groupBy(identity).mapValues(_.size)).reduce(_ |+| _) // Map(1 -> 1, 2 -> 2, 3 -> 3, 8 -> 1, 4 -> 2)
Find a number available in first array with numbers in second. If number not found get the immediate lower.
val a = List(1,2,3,4,5,6,7,8,9)
val b = List(1,5,10)
expected output after comparing a with b
1 --> 1
2 --> 1
3 --> 1
4 --> 1
5 --> 5
6 --> 5
7 --> 5
8 --> 5
9 --> 5
Thanks
You can use TreeSet's to() and lastOption methods as follows:
val a = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
val b = List(1, 5, 10)
import scala.collection.immutable.TreeSet
// Convert list `b` to TreeSet
val bs = TreeSet(b.toSeq: _*)
a.map( x => (x, bs.to(x).lastOption.getOrElse(Int.MinValue)) ).toMap
// res1: scala.collection.immutable.Map[Int,Int] = Map(
// 5 -> 5, 1 -> 1, 6 -> 5, 9 -> 5, 2 -> 1, 7 -> 5, 3 -> 1, 8 -> 5, 4 -> 1
// )
Note that neither list a or b needs to be ordered.
UPDATE:
Starting Scala 2.13, methods to for TreeSet is replaced with rangeTo.
Here is another approach using collect function
val a = List(1,2,3,4,5,6,7,8,9)
val b = List(1,5,10)
val result = a.collect{
case e if(b.filter(_<=e).size>0) => e -> b.filter(_<=e).reverse.head
}
//result: List[(Int, Int)] = List((1,1), (2,1), (3,1), (4,1), (5,5), (6,5), (7,5), (8,5), (9,5))
Here for every element in a check if there is a number in b i.e. which is greater than or equal to it and reverse the filter list and get its head to make it a pair.
This question already has answers here:
Why does groupBy in Scala change the ordering of a list's items?
(2 answers)
Closed 6 years ago.
This is a very simple code that can be executed inside a Scala Worksheet also. It is a map reduce kind of approach to calculate frequency of the numbers in the list.
I am sorting the list before I am starting the groupBy and map operation. Even then the list.groupBy.map operation generates a map, which is not sorted. Neither number wise nor frequency wise
//put this code in Scala worksheet
//this list is sorted and sorted in list variable
val list = List(1,2,4,2,4,7,3,2,4).sorted
//now you can see list is sorted
list
//now applying groupBy and map operation to create frequency map
val freqMap = list.groupBy(x => x) map{ case(k,v) => k-> v.length }
freqMap
groupBy doesn't guarantee any order
val list = List(1,2,4,2,4,7,3,2,4).sorted
val freqMap = list.groupBy(x => x)
Output:
freqMap: scala.collection.immutable.Map[Int,List[Int]] = Map(1 -> List(1), 2 -> List(2, 2, 2), 7 -> List(7), 3 -> List(3), 4 -> List(4, 4, 4))
groupBy takes the list and groups the elements. It builds a Map in which the
key is a unique value of the list
value is a List of all occurrence in the list.
Here is the official method definition from the Scala docs:
def groupBy [K] (f: (A) ⇒ K): Map[K, Traversable[A]]
If you want to order the grouped result you can do it with ListMap:
scala> val freqMap = list.groupBy(x => x)
freqMap: scala.collection.immutable.Map[Int,List[Int]] = Map(1 -> List(1), 2 -> List(2, 2, 2), 7 -> List(7), 3 -> List(3), 4 -> List(4, 4, 4))
scala> import scala.collection.immutable.ListMap
import scala.collection.immutable.ListMap
scala> ListMap(freqMap.toSeq.sortBy(_._1):_*)
res0: scala.collection.immutable.ListMap[Int,List[Int]] = Map(1 -> List(1), 2 -> List(2, 2, 2), 3 -> List(3), 4 -> List(4, 4, 4), 7 -> List(7))
I am learning scala, and at one point I want to remove entries from a map based on the value (not the key). I also want to know how many entries were removed - my program expects that exactly one entry should be removed.
Removing entries by their values can be done by applying filterNot, ok -- but how can I verify that exactly one entry was removed?
So far the only way I saw to achieve that is to run the predicate twice -- once for the "count" method (to count how often the predicate matches), and then with filterNot, to actually remove the entries.
What is the Scala way of achieving that in one go?
The only other solution I found is to first use filter(...) to get the values to be removed ,and then use "-" to throw out the elements by their keys - but again, this requires two runs.
As long as you don't mind creating a collection out of the information that is removed you can use partition:
scala> Map(1 -> 1, 2 -> 2, 3 -> 3)
res0: scala.collection.immutable.Map[Int,Int] = Map(1 -> 1, 2 -> 2, 3 -> 3)
scala> res0.partition { case (k, v) => v % 2 == 0 }
res3: (Map(2 -> 2),Map(1 -> 1, 3 -> 3))
If you just want to know if only one entry was removed then you can use size to get the size before and after the filter operation. The difference should be one.
scala> Map(1 -> 1, 2 -> 2, 3 -> 3)
res0: scala.collection.immutable.Map[Int,Int] = Map(1 -> 1, 2 -> 2, 3 -> 3)
scala> res0.filterNot { case (k, v) => v % 2 == 0 }
res1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 1, 3 -> 3)
scala> res0.size - res1.size
res2: Int = 1
Consider groupBy on the mapped values, as follows; let
val a = ((1 to 3) zip (11 to 33)).toMap
a: Map(1 -> 11, 2 -> 12, 3 -> 13)
then
a.groupBy(_._2 % 2 == 0)
res: Map(false -> Map(1 -> 11, 3 -> 13), true -> Map(2 -> 12))
I have a list
val data = List(2, 4, 3, 2, 1, 1, 1,7)
with which I want to create a map such that values in above list are keys to new one with indeces as new values I tried
scala> data.zipWithIndex.toMap
res5: scala.collection.immutable.Map[Int,Int] = Map(1 -> 6, 2 -> 3, 7 -> 7, 3 -> 2, 4 -> 1)
but strangely it gives res5(1) as 6 but I want it to be 4.
I could solve it by
data.zipWithIndex groupBy (_._1) mapValues (w=>w.map(tuple=>tuple._2) min)
but is there any way I can pass a function f to toMap so that it creates map in desired way.
toMap is going to add each pair to the map in the order of the zipped list, and when you add a mapping k -> v to a map that already contains a k, the old value is simply replaced.
An easy fix is just to reverse the list after zipping the indices and before converting to a map:
data.zipWithIndex.reverse.toMap
Now the mappings 1 -> 6 and 1 -> 5 will be added before 1 -> 4, which means 1 -> 4 is the one you'll see in the result.