简体   繁体   English

Scala-通过公共元素合并两个元组列表

[英]Scala - Merge two lists of tuples by common elements

How to merge two lists of tuples that simulates Chasles' Relation? 如何合并两个模拟 Chasles关系的元组列表?

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

Here is an example: 这是一个例子:

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

Expected result would be: 预期结果将是:

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

You basically want to consider all pairs of one element from the first list and one from the second and keep those where the "b" elements match. 基本上,您要考虑第一个列表中所有元素对和第二个列表中所有元素对,并保持匹配那些“ b”个元素。

In other words, we want to map over l1 and, inside that map, map over l2 , meaning we consider all the pairs of an element from each list, so something like: 换句话说,我们要在l1进行映射,并且在该映射内,在l2上进行映射,这意味着我们考虑了每个列表中元素的所有对,因此类似:

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

That's not quite right, though, since we now have a List[List[((String, String),(String,String))]] --we needed to flatmap: 不过,这还不太正确,因为我们现在有了一个List[List[((String, String),(String,String))]]我们需要平面映射:

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

Now we have to filter to keep just the pairs we want and tidy up: 现在我们必须进行过滤以保持我们想要的对并整理:

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

which gives us 这给了我们

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

That was kind of an ugly mess, so and we can tidy it up a bit--let's filter l2 in our original flatmap and build the result there, so we don't have to juggle the tuple of tuples: 那是一个丑陋的混乱,所以我们可以对其进行整理—让我们在原始flatmap过滤器l2并在那里建立结果,因此我们不必flatmap元组的元组:

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

This is one of those cases where it's easier to read a for comprehension: 这是那些情况下更易于阅读一个for理解:

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

For each tuple in l1 you can filter l2 to select the tuples with the matching first element: 对于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  
}

You could also convert l2 into a Map beforehand for better selection performance: 您也可以事先将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  
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM