Reputation: 21
I have a
finals = Set[(String, String)]
and an
init = Array[(String, String), String]
What I am trying to do is if a (String, String)
of the array init, is equal to a tuple of the finals set, add it in a Map[(String, String), String]
.
For example finals is:
Set[(m1, c1), (m2, c1)]
and init is:
Array[[(m1, c1), n], [(m2, c2), l], (m2, c1), k]]
The result would be:
Map[((m1, c1), n), ((m2, c1), k)]
My question is if nested foreach
are correct in scala? As far as i wrote the code for iterating both of the structures simultaneously, there was no error and it worked, but i don't know if it is a functional way.
My code is:
init.foreach(i => {
finals.foreach(j => {
if (i._1.equals(j))
{ counts += (i._1 -> i._2) }
})
})
Thanks in advance!
Upvotes: 1
Views: 3080
Reputation: 27356
Here is another way of writing this code
counts = init.filter(e => finals.contains(e._1)).toMap
The best way to think about this sort of problem is to look at the basic operations that you are performing and work out the equivalent operation in Scala.
The main operation you require is to select specific elements of a collection called init
, and the Scala operation for that is filter
.
The test you are applying is to see if the first tuple is in another collection, and the Scala operation for that is contains
.
Finally, you want to convert a collection of tuples into a Map
, and the Scala operation for that is toMap
.
Upvotes: 2
Reputation: 9375
foreach
can be functional, but counts += ...
is not - as a side-effect, it mutates the counts
data structure.
Here's a rather longer answer that results in a one-liner, but I'm trying to explain the thought process:
When struggling to come up with a functional way of writing the code, I often find it helpful to think about how I might for example use...
In this case, if you e.g. think about how you might do a map
, you could rephrase this:
What I am trying to do is if a (String, String) of the array finals, is equal to a tuple of the init set, add it in a Map[(String, String), String].
(I assume you accidentally swapped finals/init in your question but had them the right way round in the code bit)
as:
What I'm trying to do is map over the (String, String)
items in the array finals
, and if one of them is in init
, return the found item. The returned value is stored in counts
.
However, in this case, that doesn't quite get you what you want - map
requires you to return exactly 1 item for each item you map over, so you need to change to flatMap
to avoid that constraint.
val finals: Set[(String, String)] = Set(("m1", "c1"), ("m2", "c1"))
val init: Array[((String, String), String)] = Array((("m1", "c1"), "n"), (("m2", "c2"), "l"), (("m2", "c1"), "k"))
val counts = finals.flatMap( finals_item => init.filter(_._1 == finals_item))
/*
Set[((String, String), String)] = Set(
(("m1", "c1"), "n"),
(("m2", "c1"), "k")
)
*/
Then you just add .toMap
:
finals.flatMap( finals_item => init.filter(_._1 == finals_item)).toMap
Upvotes: 1
Reputation: 1568
Here is a functional way of using it using collect
scala> val x=Set(("m1", "c1"), ("m2", "c1"))
//x: scala.collection.immutable.Set[(String, String)] = Set((m1,c1), (m2,c1))
scala> val y = Array((("m1", "c1"), "n"), (("m2", "c2"), "l"), (("m2", "c1"), "k"))
//y: Array[((String, String), String)] = Array(((m1,c1),n), ((m2,c2),l), ((m2,c1),k))
scala> y.collect{
case e if x.filter(p=> p._1 == e._1._1 && p._2 == e._1._2).size>0 => e
}
//res3: Array[((String, String), String)] = Array(((m1,c1),n), ((m2,c1),k))
Upvotes: 0