1

I'm working on this scala code:

 case class M(a: String, b: Int) val mm1 = List(Map("a" -> "001", "b" -> 12), Map("a" -> "002", "b" -> 25), Map("a" -> "003", "b" -> 100)) val mm2 = List(M("001", 12), M("004", 99), M("003", 100)) def validate(mm1: List[Map[String, Any]], mm2: List[M]): Boolean = { for (m1 <- mm1) { var found = false val a = m1("a") // nested loop: iterate mm2 breakable { for (m2 <- mm2) { if (a == m2.a) { assert(m1("b") == m2.b) found = true break // if found just break } } } // All of elements in mm1 must exist in mm2. // We will know this if "found" variable is set to true. assert(found == true) } true } 

Basically the validate method just make sure all of value in mm1 exist in mm2 (based on "text" key/property), the other way around is not necessary. Then it also make sure the properties value of each element of mm1 and mm2 are equal.

I'm trying to convert the validate method in functional programming style. But don't know how to deal with the checking of all of mm1 item should be in mm2.

Here what I tried with for-comprehension:

 def validate2(mm1: List[Map[String, Any]], mm2: List[M]): Boolean = { for { m1 <- mm1 a = m1("a") m2 <- mm2 } { if (a == m2.a) { assert(m1("b") == m2.b) } } true } 

Not really good, since I can't put var found = false inside the for comprehension. So I can't do a checking if all items in mm1 exist in mm2.

Anyone can help or have better idea (maybe using recursion or without using var found at all) ?

2 Answers 2

2

This can be written pretty clearly (to my eye) using forall (which takes a predicate and returns true if all the elements in the collection satisfy it) and exists (which also takes a predicate, but only requires one match):

def validate(mm1: List[Map[String, Any]], mm2: List[M]) = mm1.forall { m1 => mm2.exists { case M(a, b) => a == m1("a") && b == m1("b") } } 

Note that I'm not using assert to fail with an exception on invalid input—this is inherently not functional, and it breaks the implicit contract of the method's type signature—if I see a validate(...) method that returns a boolean value, I'm going to assume that that value is true if the arguments are valid, and false if not. You could use some hybrid of exceptions and functional combinators to implement the same behavior as your original version, but I'd strongly suggest not doing that.

I'd also suggest avoiding lookups on the map that can fail, though (e.g. use get or getOrElse and handle the possibility of error explicitly), and not using Map[String, Any].

Sign up to request clarification or add additional context in comments.

4 Comments

The OP's code is the same. The question's about rewriting in a functional style, not optimization.
Op's code also always returns true ... yours doesn't ;)
The reason of "true" there because the code is meant to be run in Specs2. In Specs2, you have to return a result (like boolean true for example if the test is success).
@suud Then perhaps use the must beTrue matcher
0

Consider something like this, perhaps:

val ms = mm2.map(m => m.a -> m.b).toMap mm1.foreach { m => assert(m.get(a).flatMap(ms.get).exists(_ == m("b"))) } true 

4 Comments

Sorry, I'm not sure exactly what you're aiming for here, but I don't think it works, and using "" as some kind of sentinel value without being extremely explicit that that's what you're doing is a bad idea.
good point. removed the sentinel ... Why do say it doesn't work though? I am pretty sure it does.
It'll always return true, for one thing.
Yes, except when assert fails. Just like in the OP's request ... Your version is actually wrong in that respect ;)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.