I have an animal class defined as
case class Animal(name: String, properties: List[String])
Given a list of Animals, I want a map from property -> list of animals that satisfy that property
As an example, if I have input as, say,
List(
Animal("Dog",
List("has tail",
"can swim",
"can bark",
"can bite")),
Animal("Tuna",
List("can swim",
"has scales",
"is edible")),
Animal("Black Mamba",
List("has scales",
"is venomous",
"can bite"))
)
The output should be
Map(
"has tail" -> List(Dog)
"can swim" -> List(Tuna,Dog)
"can bark" -> List(Dog)
"has scales" -> List(Tuna,Snake)
"is edible" -> List(Tuna)
"is venomous" -> List(Snake)
"can bite" -> List(Dog,Snake)
)
I am pretty new to functional programming. I can do this in an imperative manner, but have been struggling to come up with a functional solution to it. Any pointers are most welcome! :)
You want to get a list of key-value pairs to start. We can start this problem by first seeing how we would covert a single Animal
to a list of key-value pairs. You may have heard of the map
function. That allows you to transform lists and other basic structures by applying a function to each element in the list. We can use it to good effect here:
animal.properties.map(property => (property, animal.name))
Here we're taking an animal's properties
, and for each out, apply the anonymous function: property => (property, animal.name)
. This function creates a tuple (key-value pair in this case) of the property together with the animal's name.
Now we want to apply this to all the animals in the the list. That may sound like another map
, but then we would have a list of lists of tuples, when really we just want a list of tuples. That's when you use flatMap
which takes in a method that returns a list and applies it to each element, and flattens the list. So we just apply the above method to each element.
val kvps = animals.flatMap(animal => animal.properties.map(property => (property, animal.name))).toMap
Now we have a list of key-value pairs. Now we want to group them by their key. The groupBy
method will return a list of tuples, where the left side is a key and the right side is a list of key-value pairs. This is almost what we want, but we just want the values on the right side. So we can do:
kvps.groupBy { case (key, value) => key }.toMap.mapValues(keyValues => keyValues.map { case (key, value) => value })
Altogether it might look like:
animals.flatMap { animal =>
animal.properties map { property => (animal, property) }
}.groupBy { case (key, value) => key }.toMap mapValues { keyValues =>
keyValues map { case (key, value) => value }
}
Of course, Scala has tons of syntactic sugar that can make this method very terse:
animals.flatMap(a => a.properties.map(_ -> a.name)).groupBy(_._1).toMap.mapValues(_.map(_._2))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With