Scala: converting a list to a map

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 a property -> a list of animals that satisfy that property

As an example, if I have an input like, 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 way out 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 new to functional programming. I can do this in an imperative manner, but struggled to come up with a functional solution. Any pointers are appreciated! :)

+3


source to share


3 answers


You want to get a list of key-value pairs to start with. We can start this problem by first seeing how we will be hiding one Animal

list of key-value pairs. You may have heard of the function map

. This allows you to transform lists and other basic structures by applying a function to each item in the list. We can use it for good effect here:

animal.properties.map(property => (property, animal.name))

      

Here we take an animal properties

, and for each of them use an anonymous function: property => (property, animal.name)

. This function creates a tuple (key-value pair in this case) of the property along with the animal's name.

Now we want to apply this to all animals in the list. This may sound like another one map

, but then we'll have a list of lists of tuples when we really need a list of tuples. It's when you use flatMap

that takes 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 item.

val kvps = animals.flatMap(animal => animal.properties.map(property => (property, animal.name))).toMap

      

We now have a list of key-value pairs. Now we want to group them by their key. The method groupBy

returns a list of tuples, where the left side is the key and the right side is a list of key-value pairs. This is almost what we want, but we just need the values ​​on the right side. Thus, we can do:



kvps.groupBy { case (key, value) => key }.toMap.mapValues(keyValues => keyValues.map { case (key, value) => value })

      

In general, it might look like this:

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 concise:

animals.flatMap(a => a.properties.map(_ -> a.name)).groupBy(_._1).toMap.mapValues(_.map(_._2))

      

+2


source


One such way would be the following:

animals.
  flatMap(a => a.properties.map(p => (p, a.name)))
  .groupBy(_._1)
  .mapValues(_.map(_._2))

      

flatMap

gives you a list of property tuples -> Animal Name



groupBy

then grouped by property name, giving Map[String, List[(String,String)]

where key Map

is property and value is a tuple of property name -> animal name

Then it mapValues

takes the resulting List((String,String))

map values ​​and converts them only to the second part of the tuple, which is the name of the animal

+3


source


case class Animal(name: String, properties: List[String])

val animals = 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"))
)


animals
 .flatMap(a => a.properties.map(ab => ab -> a.name))
 .groupBy(_._1)
 .map(g => g._1 -> g._2.map(_._2)).toMap

      

The approach is as follows

  • Creating tuples (property -> animal names)
  • Group by property
  • Map tuple (property, List ((property, name))) to (property, List (name))
+1


source







All Articles