Let's say that I have a Map of strings -> List of Integers. I would like to create a function which takes in as a parameter a List of strings and returns all the integers correlating to all the string in that list. I.e. if the Map X contains the following mappings:
database = [("Bob",[1,2,3]),("John",[1,5,6]),("Trevor",[4,5,7])]
If this function takes in ["Bob","John"] as the list of names, it should return,
[1,2,3,5,6]
Since Bob correlates to 1,2,3 and John correlates to 1,5,6 (same entries for both names aren't duplicated). I also would like to not introduce a mutable variable if I don't have to, thus leading me to believe a for comprehension that yields this list of number values would be the best way to achieve this, but I'm not sure how.
If you want to use a for-comprehension you can so this:
val result = for {
key <- keys
nums <- map.get(key).toSeq
num <- nums
} yield num
result.distinct
Explanation:
for each key in the list try to get an entry and convert it to a Seq (necessary because flatMap expects a Seq in this case) and add every number in the list to the result. If the key is not present in the map the collection will be empty and therefore not yield any results. At the end call distinct to remove the duplicates.
val myMap = Map("Bob" -> List(1,2,3), "John" -> List(1,5,6), "Trevor" -> List(4,5,7))
val names = List("Bob", "John")
You can add default value to Map using method withDefaultValue:
val mapWithDefaul = myMap withDefaultValue Nil
Then you could use Map as function in flatMap:
names.flatMap(mapWithDefaul).distinct
// List(1, 2, 3, 5, 6)
Let
val db = Map("Bob" -> List(1,2,3), "John" -> List(1,5,6), "Trevor" -> List(4,5,7))
val names = List("Bob", "John")
Then a similar approach to #senia's using flatMap,
implicit class mapCorr[A,B](val db: Map[A,List[B]]) extends AnyVal {
def corr(keys: List[A]): List[B] = {
keys.flatMap{ k => db get k }.flatten.distinct
}
}
and
scala> db.corr(keys)
res0: List[Int] = List(1, 2, 3, 5, 6)
Here we allow for key lists of type A and maps from type A to type List[B] .
val myset = Set("Bob","John")
val database = Map(("Bob"->List(1,2,3)),("John"->List(1,5,6)),("Trevor"->List(4,5,7)))
val ids = database.filter(m => myset.contains(m._1)).map(_._2).flatten.toList.distinct
outputs:
ids: List[Int] = List(1, 2, 3, 5, 6)
Something like:
val result = database.filter(elem => list.contains(elem._1)).foldLeft(List())((res,elem) => res ++ elem._2)
where list is the input list of names.
Related
I know that it's possible to easily pattern match against the head (or an arbitrary number of initial elements) and tail of a List:
val items = List(1, 2, 3, 4)
val first :: rest = items
println(first, rest) // 1, List(2, 3, 4)
However, I would like to do it the other way - can you use a pattern to get the init and last of the list?
val items = List(1, 2, 3, 4)
val rest ??? last = items
println(rest, last) // List(1, 2, 3), 4
In JavaScript this would look like:
const [...init, last] = items
You can use the :+ custom extractor object.
So the code would look like this:
val rest :+ last = items
However, note that this is equally inefficient than doing:
val last :: rest = items.reverse
But, if you then need to decompose rest again, then reversing it first will be more efficient.
Finally, remember both are unsafe since they will throw in case the List is empty.
This should work:
val xs :+ x = items
Check out: https://www.scala-lang.org/files/archive/api/2.12.0/scala/collection/Seq.html#:+(elem:A):Seq[A]
I create a list of tuples via taking a String name and matching it to an accompanying int value.
I want to be able to sum those int values in the tuple in the case that there are multiple strings of the same name. My current approach follows this utilization of groupby which if I understand right is returning me a Map with keys based upon _ . _ 1 and list of values:
def mostPopular(data: List[List[String]]): (String, Int) = {
//take the data and create a list[(String,Int)]
val nameSums = data.map(x => x(1) -> x(2).toInt)
//sum the values in _._2 based on same elements in _._1
val grouped = nameSums.groupBy(_._1).foldLeft(0)(_+_._2)
}
I've seen other solution that have dealt with averaging different values of tuples but they haven't explained how to sum values that fall under the same name
In your case value (see below code snippet) is a list of (String, Int) do value.map(_._2).sum or value.foldLeft(0)((r, c) => r + (c._2))
nameSums.groupBy(_._1).map { case (key, value) => key -> (value.map(_._2)).sum}
Scala REPL
scala> val nameSums = List(("apple", 10), ("ball", 20), ("apple", 20), ("cat", 100))
nameSums: List[(String, Int)] = List((apple,10), (ball,20), (apple,20), (cat,100))
scala> nameSums.groupBy(_._1).map { case (key, value) => key -> (value.map(_._2)).sum}
res15: scala.collection.immutable.Map[String,Int] = Map(cat -> 100, apple -> 30, ball -> 20)
I have a scala list and a funcion:
val l = List((1,2), (3,4), (5,6))
def f(x:Int, y:Int) = x+y
I want to build a dictionary that associated to each tuple its sum.
The result f(1,2) should be kept in memory and not recomputed each time that it is called. How can I do that? In python I would use a dictionary.
l.map(x => x -> (f _).tupled(x)).toMap
An analog to Python dictionaries in Scala is Map:
λ val resultsMap = Map( // Create a map from a Seq of (key, value) pairs
l.zip( // List(((1, 2), SOMETHING), ((3, 4), SOMETHING), etc.)
l.map((f _).tupled) // Invoke `f` on each entry in `l`
).toSeq : _*)
resultsMap: Map[(Int, Int), Int] = Map((1, 2) -> 3, (3, 4) -> 7, (5, 6) -> 11)
Usage:
λ resultsMap((1, 2))
res0: Int = 3
Using a for comprehension we can extract the tuple values as well as bind each tuple for constructing the associations, like this,
for ( t#(a,b) <- l ) yield t -> f(a,b)
Thus applying toMap on the collection from the comprehension we get the desired map. In this approach though we must declare as many items in the tuple as we wish to be applied to the function.
I have a list of lists with the following data
val list = List(List("a","b","c"),List("d","e","f"),List("a","a","a"))
I want to disicover how many different data do I have in each position of the sublists
1 -> List("a","d","a")
2 -> List("b","e","a")
3 -> List("c","f","a")
Is there a way to do that? It doesn't need to be indexed, but I need the amount of different values per sublist index, the result could also be
2 // different values a and d
3 // different values b, e and a
3 // different values c, f and a
As I noted in a comment on Jhonny Everson's (almost right but now deleted) answer, you can use transpose to get a list of the columns, and then distinct to remove duplicates:
scala> list.transpose.map(_.distinct.size)
res0: List[Int] = List(2, 3, 3)
This will throw an exception if all the lists don't have the same size, but that's probably what you want.
scala> list.transpose.map(_.toSet.size)
res0: List[Int] = List(2, 3, 3)
The output of list.transpose is List(List(a, d, a), List(b, e, a), List(c, f, a)) which gives you the structure you want then map each List to a Set to get the unique elements and count them.
If you want unique elements per position, then you can use zipWithIndex and reverse the outputs.
scala> list.transpose.map(_.distinct).zipWithIndex.map(t => t._2 -> t._1)
res1: List[(Int, List[String])] = List((0,List(a, d)), (1,List(b, e, a)), (2,List(c, f, a)))
Here is the code you wanted:
var lOfl = List(List.range(1,5), List(2,2,2,3), Nil)
//> lOfl : List[List[Int]] = List(List(1, 2, 3, 4), List(2, 2, 2, 3), List())
for {
l <- lOfl
} yield l.toSet.count(_ => true) //> res1: List[Int] = List(4, 2, 0)
A sample list of list of Int is created as lOfl; for loop iterates through each list of Int; toSet converts list to set eliminating duplicate elements. then count function does as the name says it all.
OR use the Scala's most used collection function (:P :P), map, like below,
lOfl.map(l => l.toSet.count(_ => true) ) //> res2: List[Int] = List(4, 2, 0)
I am trying to get the index of a row using Scala from a list consisting of lists of integers List[List[Int]]. I already have two functions that given the row index/column index and the grid as parameters, it outputs all the elements in that row. What I need is a function that given a particular element (eg: 0), it finds its row index and column index and puts them in a list: List[(Int, Int)]. I tried to code a function that gives back an index when encountering 0 and then I passed the function to the whole grid. I don't know if I'm doing it the right way. Also, I couldn't figure out how to return the list.
Also, I cannot use any loops.
Thanks in advance.
def Possibilities(): List[Int] = {
def getRowIndex(elem: Int): Int = elem match
{
case 0 => sudoku.grid.indexOf(sudoku.row(elem))
case x => x
}
val result1 = sudoku.grid map {row => row map getRowIndex}
}
I think with two dimensions it is much easier to write such a method with for comprehensions.
Given a List[List[Int]] like this:
val grid = List(
List(1, 2, 3),
List(4, 5, 6),
List(3, 2, 1))
we can simply walk through all the rows and columns, and check whether each element is the one we are looking for:
def possibilities(findElem: Int): List[(Int, Int)] = {
for (
(row, rowIndex) <- grid.zipWithIndex;
(elem, colIndex) <- row.zipWithIndex
if elem == findElem
) yield (rowIndex, colIndex)
}
The yield keyword creates a collection of the results of the for loop. You can find more details on Scala's forloop syntax here (and a more thorough discussion on how this relates to map, flatMap, etc. here).
So, if you don't want to use a for loop, simply 'translate' it into an equivalent expression using map. flatMap, and withFilter:
def possibilities(findElem: Int): List[(Int, Int)] = {
grid.zipWithIndex flatMap { rowAndIndex =>
rowAndIndex._1.zipWithIndex.withFilter(_._1 == findElem) map { colAndIndex =>
(rowAndIndex._2, colAndIndex._2)
}
}
}
Step 1, create a collection of all possible tuples of indices, with a for comprehension (for looks like a loop but it is not)
val tuples = for (i <- grid.indices; j <- grid.head.indices) yield (i, j)
Step 2, filter this collection
tuples.filter { case (i, j) => grid(i)(j) == valueToFind }