【问题标题】:Scala - Merge two lists of tuples by common elementsScala - 通过公共元素合并两个元组列表
【发布时间】:2018-05-06 20:35:21
【问题描述】:

如何合并两个模拟 Chasles 关系的元组列表?

(a, b), (b, c) => (a, c)

这是一个例子:

val l1 = List(("Dan", "b"), ("Dan","a"), ("Bart", "c"))
val l2 = List(("a", "1"), ("c", "1"), ("b", "3"), ("a", "2"))

预期结果是:

val result = List(("Dan", "3"), ("Dan", "1"), ("Dan", "2"), ("Bart", "1"))

【问题讨论】:

  • 到目前为止你尝试了什么?
  • l1 中的元素有时是否也比 l2 多?

标签: scala list group-by scala-collections


【解决方案1】:

您基本上要考虑第一个列表中的一个元素和第二个列表中的所有元素对,并保留“b”元素匹配的那些。

换句话说,我们想要映射到l1,并且在该映射内部,映射到l2,这意味着我们会考虑每个列表中的所有元素对,例如:

l1.map(x => l2.map(y => (x,y))

不过,这并不完全正确,因为我们现在有一个 List[List[((String, String),(String,String))]]——我们需要平面映射:

l1.flatMap(x => l2.map(y => (x,y)))

现在我们必须过滤以保留我们想要的配对并整理:

l1.flatMap(x => l2.map(y => (x,y)))
  .filter{ case ((_,y),(b,_)) => y == b }
  .map {case ((x, _),(_,c)) => (x,c) }

这给了我们

List((Dan,3), (Dan,1), (Dan,2), (Bart,1))

那真是一团糟,所以我们可以稍微整理一下——让我们在原来的 flatmap 中过滤 l2 并在那里构建结果,这样我们就不必处​​理元组:

l1.map{ case (x,y) => 
    l2.filter{ case (b, _) => y == b}
      .map{ case (_, c) => (x, c)} }

这是更容易阅读for理解的情况之一:

for {
  (x, y) <- l1
  (b, c) <- l2
  if y == b
} yield (x,c)

【讨论】:

  • 不错的方法,我没有看到这个问题。谢谢你。
【解决方案2】:

对于l1 中的每个元组,您可以过滤l2 以选择具有匹配第一个元素的元组:

def join[A, B, C](l1: List[(A, B)], l2: List[(B, C)]): List[(A, C)] = {
  for {
    (key, subkey) <- l1
    value <- l2.collect { case (`subkey`, value) => value }
  } yield key -> value  
}

您也可以预先将l2 转换为Map 以获得更好的选择性能:

def join[A, B, C](l1: List[(A, B)], l2: List[(B, C)]): List[(A, C)] = {
  val valuesMap = l2.groupBy(_._1)
  for {
    (key, subkey) <- l1
    (_, value) <- valuesMap.getOrElse(subkey, Nil)
  } yield key -> value  
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-07
    • 2017-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多