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

#scala

#scala

Вопрос:

У меня есть список с некоторыми строками, и я хотел бы объединить / конкатенировать строки, только если длина символов в строках равна <= определенному пределу.

Например, у нас есть список строк:

 val xs = List("This is a sentence in index0.", "This is a short sentence.", "This is a very very very loooooooooong sentence.", "This is another short sentence.", "Another one.", "The end!")
  

Предел объединения равен 60, это означает, что мы должны посмотреть на длины символов в строке, прежде чем объединять ее со следующей строкой и гарантировать, что длина символа не превышает 60. если результат слияния превысит 60, вы не объединяете, а просто берете элемент / строку как есть, переходите к следующему элементу и пытаетесь объединить со следующим.

Итак, если мы возьмем наш приведенный выше список,

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

 xs.map(_.length)
res: List[Int] = List(29, 25, 48, 31, 12, 8)
  

Исходя из этого, мы можем объединить только строки с индексом 0 и 1, оставить индекс 2 как есть и объединить строки с индексом 3, 4 и 5.
Результирующий список строк теперь должен выглядеть следующим образом:

 val result = List("This is a sentence in index0.This is a short sentence.", "This is a very very very loooooooooong sentence.", "This is another short sentence.Another one.The end!")
  

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

Ответ №1:

Я взломал этот фрагмент кода:

 val xs = List(
  "This is a sentence in index0.",
  "This is a short sentence.",
  "This is a very very very loooooooooong sentence.",
  "This is another short sentence.",
  "Another one.",
  "The end!")

println(concatLimited(xs, 60))

def concatLimited(list: List[String], limit: Int): List[String] = {
  def concat(left: List[String], middle: String, right: List[String]): List[String] = {
    if (right.isEmpty) {
      left :  middle
    }
    else {
      if (middle.length   right.head.length < limit) {
        concat(left, middle   right.head, right.tail)
      }
      else {
        concat(left :  middle, right.head, right.tail)
      }
    }
  }

  if (list.isEmpty) List()
  else concat(List(), list.head, list.tail)
}
  

Попробуйте!

Он использует рекурсивную функцию, которая удаляет элементы из заголовка списка (здесь: right ) и собирает их в параметре middle до тех пор, пока он не превысит ваш размер, затем он добавляется middle к результирующему списку left и начинается с нового middle , пока right не станет пустым.


Я надеюсь, что это поможет.

Ответ №2:

Что касается эффективной конкатенации строк, эта реализация concatenate продолжает находить диапазоны индексов, которые соответствуют критериям ( getNext сообщает начальный индекс следующего подмножества), и суммирует их с помощью StringBuilder и, наконец, выдает все объединенные строки.

 import scala.annotation.tailrec
import scala.collection.mutable.ListBuffer

object ListConcatenation extends App {
  val xs = List(
    "This is a sentence in index0.",
    "This is a short sentence.",
    "This is a very very very loooooooooong sentence.",
    "This is another short sentence.",
    "Another one.",
    "The end!")

  concatenate(xs, 60).foreach(println)

  def concatenate(values: List[String], limit: Int): List[String] = {
    def getNext(start: Int): Int = {
      @tailrec
      def getNext(sum: Int, index: Int): Int = {
        if (index >= values.length)
          index
        else {
          val next = sum   values(index).length

          if (next > limit)
            index
          else
            getNext(next, index   1)
        }
      }

      getNext(0, start)
    }

    if (values == null || values.length <= 1)
      values
    else {
      val result = new ListBuffer[String]
      var head = 0

      do {
        val next = getNext(head)

        val builder = new StringBuilder

        (head until next)
          .map(values)
          .foreach(builder.append)

        result  = builder.toString()

        head = next
      }
      while (head < values.length)

      result.toList
    }
  }
}