BuffaloSoldier
BuffaloSoldier

Reputation: 21

Is iterate with nested foreach in scala functional?

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

Answers (3)

Tim
Tim

Reputation: 27356

Here is another way of writing this code

counts = init.filter(e => finals.contains(e._1)).toMap

Explanation

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

m01
m01

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...

  • recursion
  • more advanced operations (map, flatMap, reduce, filter, fold, ...)

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

Puneeth Reddy V
Puneeth Reddy V

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

Related Questions