Объединение списка кортежей в Scala на основе ключа - PullRequest
0 голосов
/ 25 сентября 2019

У меня список кортежей выглядит следующим образом:

Seq("ptxt"->"how","list"->"you doing","ptxt"->"whats up","ptxt"-> "this ","list"->"is ","list"->"cool")

На клавишах объедините ptxt со всем списком, который последует за ним.например, создать новый seq, похожий на этот:

Seq("how you doing", "whats up", "this is cool")

Ответы [ 4 ]

6 голосов
/ 25 сентября 2019

Вы можете сбросить ваш Seq с foldLeft:

val s = Seq("ptxt"->"how ","list"->"you doing","ptxt"->"whats up","ptxt"-> "this ","list"->"is ","list"->"cool")

val r: Seq[String] = s.foldLeft(List[String]()) {
  case (xs, ("ptxt", s)) => s :: xs
  case (x :: xs, ("list", s)) => (x + s) :: xs
}.reverse

Если вас не волнует заказ, вы можете опустить reverse.


Функция foldLeft принимает два аргумента: первое - это начальное значение, а второй - это функция, принимающая два аргумента: предыдущий результат и элемент последовательности.Результат этого метода затем подается следующим вызовом функции в качестве первого аргумента.

Например, для чисел foldLeft, будет просто создать сумму всех элементов, начиная слева.

List(5, 4, 8, 6, 2).foldLeft(0) { (result, i) =>
  result + i
} // 25

В нашем случае мы начинаем с пустого списка.Затем мы предоставляем функцию, которая обрабатывает два случая, используя сопоставление с образцом.

  • Случай, когда клавиша "ptxt".В этом случае мы просто добавляем значение к списку.

    case (xs, ("ptxt", s)) => s :: xs
    
  • Случай, когда ключом является «список».Здесь мы берем первую строку из списка (используя сопоставление с образцом) и затем соединяем значение с ней, после чего мы помещаем ее обратно с остальной частью списка.

    case (x :: xs, ("list", s)) => (x + s) :: xs
    

Atконец, так как мы добавляли элемент, нам нужно вернуть наш список.Почему мы готовились, а не добавлялись?Потому что append в неизменяемом списке - O(n), а prepend - O(1), поэтому он более эффективен.

3 голосов
/ 25 сентября 2019

Здесь другое решение:

val data = Seq("ptxt"->"how","list"->"you doing","ptxt"->"whats", "list" -> "up","ptxt"-> "this ", "list"->"is cool")

Ключи и значения первой группы:

val grouped = s.groupBy(_._1)
               .map{case (k, l) => k -> l.map{case (_, v) => v.trim}}

// > Map(list -> List(you doing, up, is cool), ptxt -> List(how, whats, this))

Затем архивируйте и объедините два значения:

grouped("ptxt").zip(grouped("list"))
    .map{case (a, b) => s"$a $b"}

// > List(how you doing, whats up, this is cool)

Отказ от ответственности : Это работает, только если в списке всегда есть key, value, key, value,.. - мне пришлось настроить входные данные.

2 голосов
/ 25 сентября 2019

Если вы измените Seq на List, вы можете решить это с помощью простой хвостовой рекурсивной функции.
(код использует Scala 2.13, но может быть переписан виспользуйте более старые версии Scala, если необходимо)

def mergeByKey[K](list: List[(K, String)]): List[String] = {
  @annotation.tailrec
  def loop(remaining: List[(K, String)], acc: Map[K, StringBuilder]): List[String] =
    remaining match {
      case Nil =>
        acc.valuesIterator.map(_.result()).toList

      case (key, value) :: tail =>
        loop(
          remaining = tail,
          acc.updatedWith(key) {
            case None           => Some(new StringBuilder(value))
            case Some(oldValue) => Some(oldValue.append(value))
          }
        )
    }
  loop(remaining = list, acc = Map.empty)
}

val data = List("ptxt"->"how","list"->"you doing","ptxt"->"whats up","ptxt"-> "this ","list"->"is ","list"->"cool")
mergeByKey(data)
// res: List[String] = List("howwhats upthis ", "you doingis cool")

Или один вкладыш, использующий groupMap.
(вдохновленный ответом pme)

data.groupMap(_._1)(_._2).view.mapValues(_.mkString).valuesIterator.toList
1 голос
/ 25 сентября 2019

Добавление другого ответа, поскольку у меня недостаточно очков репутации для добавления комментария.просто улучшение ответа Кшиштофа Атласика.чтобы компенсировать случай, когда Seq начинается со «списка», вы можете добавить еще один случай как:

  case (xs,("list", s)) if xs.isEmpty=>xs

Таким образом, конечный код может выглядеть примерно так:

val s = Seq("list"->"how ","list"->"you doing","ptxt"->"whats up","ptxt"-> "this ","list"->"is ","list"->"cool")

val r: Seq[String] = s.foldLeft(List[String]()) {
  case (xs,("list", s)) if xs.isEmpty=>xs
  case (xs, ("ptxt", s)) => s :: xs
  case (x :: xs, ("list", s)) => (x + s) :: xs
}.reverse
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...