Scala:将列表转换为地图

iTw*_*nty 5 dictionary functional-programming scala list

我有一个动物类定义为

case class Animal(name: String, properties: List[String])
Run Code Online (Sandbox Code Playgroud)

给定一个动物列表,我想要一张来自属性的地图 -> 满足该属性的动物列表

举个例子,如果我输入的是,

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"))
)
Run Code Online (Sandbox Code Playgroud)

输出应该是

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)
)
Run Code Online (Sandbox Code Playgroud)

我对函数式编程还很陌生。我可以以命令式的方式做到这一点,但一直在努力想出一个功能性的解决方案。欢迎任何指点!:)

Ben*_*ich 4

您想要获取一个键值对列表来开始。我们可以首先看看如何将单个值转换Animal为键值对列表来开始这个问题。您可能听说过该map功能。这允许您通过将函数应用于列表中的每个元素来转换列表和其他基本结构。我们可以在这里使用它来达到良好的效果:

animal.properties.map(property => (property, animal.name))
Run Code Online (Sandbox Code Playgroud)

在这里,我们获取动物的properties,并对每个动物应用匿名函数:property => (property, animal.name)。此函数创建属性的元组(在本例中为键值对)以及动物的名称。

现在我们想将其应用于列表中的所有动物。这可能听起来像另一个map,但是我们会得到一个元组列表的列表,而实际上我们只想要一个元组列表。这时您可以使用flatMapwhich 接受一个返回列表的方法并将其应用于每个元素,然后展平列表。所以我们只需将上述方法应用到每个元素即可。

val kvps = animals.flatMap(animal => animal.properties.map(property => (property, animal.name))).toMap
Run Code Online (Sandbox Code Playgroud)

现在我们有一个键值对列表。现在我们想按它们的键对它们进行分组。该groupBy方法将返回一个元组列表,其中左侧是键,右侧是键值对列表。这几乎就是我们想要的,但我们只想要右侧的值。所以我们可以这样做:

kvps.groupBy { case (key, value) => key }.toMap.mapValues(keyValues => keyValues.map { case (key, value) => value })
Run Code Online (Sandbox Code Playgroud)

总的来说,它可能看起来像:

animals.flatMap { animal =>
    animal.properties map { property => (animal, property) }
}.groupBy { case (key, value) => key }.toMap mapValues { keyValues =>
    keyValues map { case (key, value) => value }
}
Run Code Online (Sandbox Code Playgroud)

当然,Scala 有大量的语法糖可以使这个方法非常简洁:

animals.flatMap(a => a.properties.map(_ -> a.name)).groupBy(_._1).toMap.mapValues(_.map(_._2))
Run Code Online (Sandbox Code Playgroud)