Как объединить два списка кортежей?

у меня есть два списка в Scala, как объединить их так, чтобы кортежи были сгруппированы вместе?

есть ли существующий API списка Scala, который может это сделать или мне нужно сделать это самостоятельно?

вход:

 List((a,4), (b,1), (c,1), (d,1))
 List((a,1), (b,1), (c,1))

ожидаемый результат:

List((a,5),(b,2),(c,2),(d,1))

3 ответов


вы можете попробовать следующие одну строку:

scala> ( l1 ++ l2 ).groupBy( _._1 ).map( kv => (kv._1, kv._2.map( _._2).sum ) ).toList
res6: List[(Symbol, Int)] = List(('a,5), ('c,2), ('b,2), ('d,1))

здесь l1 и l2 списки кортежей, которые вы хотите объединить.

теперь поломки:

  • (l1 ++ l2) вы просто объединяете оба списка
  • .groupBy( _._1) сгруппировать все Кортежи по их первому элементу. Вы получите карту с первый элемент в качестве ключа и список кортежей, начиная с этого элемента в качестве значения.
  • .map( kv => (kv._1, kv._2.map( _._2).sum ) ) вы делаете новую карту, с аналогичными ключи, но значения являются суммой всех вторых элементов.
  • .toList преобразовать результат обратно в список.

кроме того, вы можете использовать сопоставление шаблонов для доступа к элементам кортежа.

( l1 ++ l2 ).groupBy( _._1 ).map{
  case (key,tuples) => (key, tuples.map( _._2).sum ) 
}.toList

в качестве альтернативы вы можете также использовать mapValues для сокращения кода.

mapValues, Как вы, вероятно, догадываетесь, позволяет повторно отобразить только значение для каждой пары (ключ, значение) в карте, созданной groupBy.

в этом случае функция передается в mapValues уменьшает каждый (Char, Int) Кортеж только до Int, а затем суммирует результирующий список Ints.

(l1 ::: l2).groupBy(_._1).mapValues(_.map(_._2).sum).toList

если порядок списка вывода должен следовать вашему примеру, просто добавьте sorted, который опирается на Упорядочение [(Char, Int)] неявного экземпляра.

(l1 ::: l2).groupBy(_._1).mapValues(_.map(_._2).sum).toList.sorted

Если вы можете предположить, что оба List[(A,B)] упорядочены в соответствии с Ordering[A], вы можете написать что-то вроде:

def mergeLists[A,B](one:List[(A,B)], two:List[(A,B)])(op:(B,B)=>B)(implicit ord:Ordering[A]): List[(A,B)] = (one,two) match {
    case (xs, Nil) => xs
    case (Nil, ys) => ys
    case((a,b)::xs,(aa,bb)::ys) =>
      if (a == aa) (a, op(b,bb)) :: mergeLists(xs,ys)(op)(ord)
      else if (ord.lt(a,aa)) (a, b) :: mergeLists(xs, (aa,bb)::ys)(op)(ord)
      else (aa, bb) :: mergeLists((a,b) :: xs, ys)(op)(ord)
}

к сожалению, это не хвост рекурсивной.