Scala:将列表转换为地图

时间:2015-04-30 13:17:37

标签: list scala dictionary functional-programming

我有一个动物类定义为

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

鉴于动物名单,我想要一张属性的地图 - >满足该财产的动物名单

作为一个例子,如果我输入了,比方说,

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"))
)

输出应为

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

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

4 个答案:

答案 0 :(得分:3)

一种方法如下

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

flatMap为您提供了财产元组列表 - >动物名称

groupBy然后按属性名称分组,产生Map[String, List[(String,String)],其中Map的键是属性,值是属性名称的元组 - >动物名称

mapValues然后获取生成的List((String,String))地图值,并将其转换为元组的第二部分,即动物的名称

答案 1 :(得分:2)

您想要获取要启动的键值对列表。我们可以通过首先了解如何将单个Animal转换为键值对列表来解决此问题。您可能听说过map函数。这允许您通过将函数应用于列表中的每个元素来转换列表和其他基本结构。我们可以在这里使用它:

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

这里我们采用了动物的properties,并且对于每个动物都应用匿名函数:property => (property, animal.name)。此函数与属性名称一起创建属性的元组(在本例中为键值对)。

现在我们想将它应用于列表中的所有动物。这可能听起来像另一个map,但是当我们真正想要一个元组列表时,我们会得到一个元组列表列表。当你使用flatMap时,它会接收一个返回列表并将其应用于每个元素的方法,并展开列表。所以我们只是将上述方法应用于每个元素。

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

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

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

总之它可能看起来像:

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

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

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

答案 2 :(得分:1)

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

方法如下

  • 创建元组(property - > animalNames)
  • 按财产分组
  • 将元组(属性,列表((属性,名称)))映射到(属性,列表(名称))

答案 3 :(得分:0)

在Scala 2.13中,您可以使用groupMapReduces

Welcome to Scala 2.13.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_181).
Type in expressions for evaluation. Or try :help.

scala> :pa
// Entering paste mode (ctrl-D to finish)

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"))
)


(for {
  animal <- animals
  property <- animal.properties
} yield Seq(property, animal.name)).groupMapReduce(_.head)(x => List(x.last))(_ ++ _)



// Exiting paste mode, now interpreting.

class Animal

val animals: List[Animal] = 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)))

val res0: scala.collection.immutable.Map[String,List[String]] = 
HashMap(
is venomous -> List(Black Mamba), 
is edible -> List(Tuna), 
can swim -> List(Dog, Tuna), 
can bite -> List(Dog, Black Mamba), 
can bark -> List(Dog), 
has scales -> List(Tuna, Black Mamba),
has tail -> List(Dog))

转换代码为

(for {
  animal <- animals
  property <- animal.properties
} yield Seq(property, animal.name)).groupMapReduce(_.head)(x => List(x.last))(_ ++ _)

然后,让我逐行解释一下。

我们有输入数据,例如key -> List(value)的集合,我们想将其转换为类似value -> List(key)的集合

首先,我们需要将其展平为(value, key)对。这就是for表达式的作用。

for {
  animal <- animals
  property <- animal.properties
} yield Seq(property, animal.name)

它将返回类似 List(List(has tail, Dog), List(can swim, Dog), ...

的内容

然后我们应该将结果按property分组,并将分组后的值减小为List,这就是groupMapReduce的作用。

  • 在第一部分中,我们将结果按_.head分组,也就是说property是动物
  • 在第二部分中,我们通过name将分组值映射到动物的x => List(x, last),这里xSeq(property, animal.name),它仅包含两个元素,所以最后一个是name
  • 在第三部分中,我们联系所有List的动物名。这样我们就得到了最终结果。