Reducing/Folding a List of Strings to a Map[String,Boolean] in Scala - scala

I have a list like this:
val objectKeys = List("Name","Place","Animal","Thing");
I want to reduce it to a Map[String,Boolean] where Boolean is element.size < 8.
Here's what I wrote:
val mappedObject = objectKeys.fold(Map[String,Boolean])((map,key) => map + (key -> key.size < 8))
which gives me the following error:
value + is not a member of Object, but could be made available as an extension method.
and
value size is not a member of Object
My understanding about fold is that it takes a default argument and reduces the entire value around it which however doesn't seem to work in this case. Can anyone help me with this?
Ideally mappedObject should be like:
val mappedObject = Map[String,Boolean]("Name"->true,"Place"->true,"Animal"->true,"Thing"->true)
An equivalent Javascript implementation will be:
const listValues = ["Name","Place","Animal","Thing"];
const reducedObject = listValues.reduce((acc,curr) => {acc[curr] = curr.length < 8;
return acc;
},{});

If you really want to do it with a fold, that's easy to do:
objectKeys.foldLeft(Map.empty[String, Boolean]) { (acc, key) =>
acc + ((key, key.length < 8))
}
That said, I'm with Ivan on this one. map is clearly the better solution here (or fproduct if you're using the cats library).

I think in this case you should just map to a tuple containing your key with boolean check and then convert it to Map[String, Boolean] via toMap method as following.
objectKeys.map(key => (key, key.length < 8)).toMap

Related

Scala: Use map function tuples in a subsequent flatMap

I want to use a tuple of a map function in a subsequent flatMap.
Like this:
val list = orders.map(ord => (ord.prod.tasks, ord.quantity))
.zipWithIndex flatMap {
case (variable, index) =>
createSchedules(variable._1.size * variable._2, ord, null)
}
Is there a way in which I can use it or do you think that I have to change the way that I'm thinking about the solution?
I want to use a tuple of a map function in a subsequent flatMap.
Here's a working example of using tuples in a subsequent flatMap
val input = List("a", "b", "c")
val list = input.map(i => (i + "A", i + "B")).zipWithIndex flatMap{ case (variable, index)=> variable._1 + variable._2}
Issue with original code
val list = orders.map(ord => (ord.prod.tasks, ord.quantity)).zipWithIndex flatMap{case (variable, index)=>createSchedules(variable._1.size * variable._2, ord, null)}
One issue is that ord is out of scope in the 'createSchedules' call.
ord will only be visible in the initial 'orders.map' scope.
First of all, judging from the parameters that you're passing to createSchedules, it looks as if that function can be simplified to (I'm ignoring the null parameter for now):
def createSchedules(order: Order): List[Schedule] = {
val num = order.prod.tasks.size * order.quantity
// do stuff
}
Also, the initial map is unnecessary. list can be simplified to:
val list = orders.zipWithIndex
.flatMap {
case (order, index) =>
createSchedules(order)
}
It's unclear what you need the index for, though.

Adding key-value pairs to Map in scala

I am very new to scala, and want to create a hash map with the key being a candidate, and value being number of votes. Something like this: {(1:20),(2:4),(3:42),..}.
I`ve attempted with the following code:
val voteTypeList = textFile.map(x=>x(2)) //String array containing votes: [3,4,2,3,2,1,1,1,9,..]
var voteCount:Map[String,Int] = Map()
voteTypeList.foreach{x=>{
if (voteCount.contains(x)){ //Increment value
var i: Integer = voteCount(x)
voteCount.updated(x, i+1)
// print(voteCount(x))
}
else{ //Create new key-value pair
// println(x)
voteCount += (x -> 1)
}}}
print(voteCount.size)
But the voteCount does not get created and .size returns 0.
Thank you!
The problem you're encountering is caused by using a var to hold an immutable Map. Change that to a val holding a mutable Map and it works.
val voteCount:collection.mutable.Map[String,Int] = collection.mutable.Map()
Having said that, there are a number of other issues with the code that makes it non-idiomatic to the Scala way of doing things.
What you really want is something closer to this.
val voteCount = voteTypeList.groupBy(identity).mapValues(_.length)
Jwvh's answer is idiomatic but non-obvious and, if the list has very large numbers of duplicates, memory-heavy.
You might also consider the literal-minded (yet still Scalarific) way of doing this:
val voteCount = voteTypeList.foldLeft(Map(): Map[Int, Int]) { (v, x) =>
v + (x -> (v.getOrElse(x, 0) + 1))
}

Can I call a method inside of sortWith when sorting a sequence (or a map)?

I have a map Map[String,Option[Seq[String]]] and I have values for each of the string in a different map: Map[String,Option[Int]]. I am trying to map over the values and use a sortWith on the sequence but as I read online, I don't see any examples of having custom methods inside the sortWith.
How can I sort my sequence using sortWith? If I wanted to implement a custom method that returns a boolean to tell me what object is considered greater, is this possible?
val fieldMap = Map("user1" -> Seq("field1_name", "field2_name"), "user2" -> Seq("field3_name"))
val fieldValues = Map("field1_name" -> 2, "field2_name" -> 1, "field3_name" -> 3)
val sortedMap = fieldMap.mapValues(fieldList => fieldList.sortWith(fieldValues(_) < fieldValues(_)) // Scala doesn't like this
I tried:
fieldList.sortWith{(x,y) =>
val x = fieldValues(x)
val y = fieldValues(y)
x < y
}
This gives me a Type mismatch of expected type:
(String,String) => Boolean
and actual:
(String,String) => Any
EDIT Solution:
fieldList.sortWith{(x,y) =>
val x = fieldValues(x)
val y = fieldValues(x)
x.getOrElse[Double](0.0) < y.getOrElse[Double](0.0) // have to unwrap the Option.
}
You're using wrong syntax. For using sortWith you have to do something like:
fieldMap.mapValues(
fieldList => fieldList.sortWith(
(a,b) => fieldValues(a) > fieldValues(b)
)
)

Scala primitives as reference types?

Does Scala provide a means of accessing primitives by reference (e.g., on the heap) out of the box? E.g., is there an idiomatic way of making the following code return 1?:
import scala.collection.mutable
val m = new mutable.HashMap[String, Int]
var x = m.getOrElseUpdate("foo", 0)
x += 1
m.get("foo") // The map value should be 1 after the preceding update.
I expect I should be able to use a wrapper class like the following as the map's value type (thus storing pointers to the WrappedInts):
class WrappedInt(var theInt:Int)
...but I'm wondering if I'm missing a language or standard library feature.
You can't do that with primitives or their non-primitives counter parts in Java nor Scala. Don't see any other way but use the WrappedInt.
If your goal is to increment map values by key, than you can use some nicer solutions instead of wrapper.
val key = "foo"
val v = m.put(key, m.getOrElse(key, 0) + 1)
or another approach would be to set a default value 0 for the map:
val m2 = m.withDefault(_ => 0)
val v = m2.put(key, m2(key) + 1)
or add extension method updatedWith
implicit class MapExtensions[K, V](val map: Map[K, V]) extends AnyVal {
def updatedWith(key: K, default: V)(f: V => V) = {
map.put(key, f(map.getOrElse(key, default)))
}
}
val m3 = m.updatedWith("foo", 0) { _ + 1 }

Different behavior when declaration type is different(Set vs TreeSet)

var set = TreeSet(5,4,3,2,1)
println(set)
val diffSet: TreeSet[Int] = set
// if I change above code to val diffSet: Set[Int] = set
// the result is unsorted set.
for (i <- diffSet; x = i) {
println(i)
}
println("-" * 20)
// the above code translates to below and print the same result
val temp = diffSet.map(i => (i, i))
for ((i, x) <- temp) {
println(i)
}
My question is if I defined a method like this:
def genSet:Set[Int] = {
TreeSet(5, 4, 3, 2, 1)
}
and when i want to use a for loop with it
for (i <- genSet; x = i + 1) {
println(x)
}
the result is unsorted, how to fix this behavior without change the genSet's return type. if I use for loop like below, it will be fine, but I hope to keep the above code style.
for (i <- genSet) {
val x = i + 1
println(x)
}
Why the map version winds up unsorted
The map method (called with a function that we'll call func) takes an implicit CanBuildFrom parameter that takes into account the type of the collection that map is being called on, in addition to the type that func returns to choose an appropriate return type. This is used to make Map.map[Int] or BitSet.map[String] do the right thing (return general purpose lists) while Map.map[(String,Int)] or BitSet.map[Int] also do the right thing (return a Map and a BitSet) respectively.
The CanBuildFrom is chosen at compile time, so it must be chosen based on the static type of the set that you call map on (the type the compiler knows about at compile time). The static type of set is TreeSet, but the static type of diffset is Set. The dynamic type of both (at runtime) is TreeSet.
When you call map on set (a TreeSet), the compiler chooses immutable.this.SortedSet.canBuildFrom[Int](math.this.Ordering.Int) as the CanBuildFrom.
When you call map on diffset (a Set), the compiler chooses immutable.this.Set.canBuildFrom[Int] as the CanBuildFrom.
Why the for version winds up unsorted
The loop
for (i <- genSet; x = i + 1) {
println(x)
}
desugars into
genSet.map(((i) => {
val x = i.$plus(1);
scala.Tuple2(i, x)
})).foreach(((x$1) => x$1: #scala.unchecked match {
case scala.Tuple2((i # _), (x # _)) => println(x)
}))
The desugared version includes a map function which will use the unsorted CanBuildFrom as I explained above.
On the other hand, the loop
for (i <- genSet) {
val x = i + 1
println(x)
}
desugars into
genSet.foreach(((i) => {
val x = i.$plus(1);
println(x)
}))
Which doesn't use a CanBuildFrom at all, since no new collection is being returned.
Set does not guarantee ordering. Even if the underlying class is a TreeSet, if the expected result is a Set you'll loose the ordering in the first transformation you do.
If you want ordering, do not use Set. I suggest, say, SortedSet.
Change the sig of genSet to return a SortedSet
def genSet:SortedSet[Int] = {
TreeSet(5, 4, 3, 2, 1)
}
This is probably some sort of bug. I would have expected your code to work too.
I think map is the culprit. This results in the same behavior:
for (i <- genSet.map(_ + 1)) { println(i) }
And for(i <- genSet; x = i + 1) equates to for(x <- genSet.map({i => i + 1}))
You can do:
scala> for (i <-genSet.view; x = i + 1) println(x)
2
3
4
5
6
Although, it's the type of trick that when you look at it after a few months, you may wonder why you added .view ...