Объединение нескольких последовательных записей в Scala/Spark

У меня есть массив чисел, разделенных запятой, как показано на рисунке:

a:{108,109,110,112,114,115,116,118}

Мне нужен вывод примерно такой:

a:{108-110, 112, 114-116, 118}

Я пытаюсь сгруппировать непрерывные числа с «-» между ними. Например, 108 109 110 — это непрерывные числа, поэтому я получаю 108–110. 112 — отдельная запись; 114,115,116 снова представляет собой последовательность, поэтому я получаю 114-116. 118 является отдельным и рассматривается как таковой.

Я делаю это в Спарке. Я написал следующий код:

import scala.collection.mutable.ArrayBuffer

def Sample(x:String):ArrayBuffer[String]={
  val x1 = x.split(",")
  var a:Int = 0
  var present=""
  var next:Int = 0
  var yrTemp = ""
  var yrAr= ArrayBuffer[String]()
  var che:Int = 0
  var storeV = ""
  var p:Int = 0 
  var q:Int = 0

  var count:Int = 1

  while(a < x1.length)
  {
      yrTemp = x1(a)

      if(x1.length == 1)
      {
          yrAr+=x1(a)
      }
      else
      if(a < x1.length - 1)
       {
           present = x1(a)
          if(che == 0)
          {
                storeV = present
          }

          p = x1(a).toInt
          q = x1(a+1).toInt

          if(p == q)
          {
              yrTemp = yrTemp
              che = 1
          }
          else
          if(p != q)
             {
                 yrTemp = storeV + "-" + present 
                 che = 0
                 yrAr+=yrTemp
             }

       }
       else
            if(a == x1.length-1)
            {
                present = x1(a)
                yrTemp = present 
                che = 0
                yrAr+=yrTemp
            }
      a = a+1
  }
yrAr
}
val SampleUDF = udf(Sample(_:String))

Я получаю вывод следующим образом:

a:{108-108, 109-109, 110-110, 112, 114-114, 115-115, 116-116, 118}

Я не могу понять, где я ошибаюсь. Не могли бы вы помочь мне исправить это. ТИА.


person Daksh Agarwal    schedule 02.12.2017    source источник
comment
пожалуйста, объясните правила, которые приводят к ожидаемому результату. И это на самом деле не связано с искрой, это просто проблема Scala   -  person Raphael Roth    schedule 02.12.2017
comment
@RaphaelRoth Я отредактировал правило в своем вопросе.   -  person Daksh Agarwal    schedule 02.12.2017


Ответы (2)


Вот еще один способ:

def rangeToString(a: Int, b: Int) = if (a == b) s"$a" else s"$a-$b"

def reduce(xs: Seq[Int], min: Int, max: Int, ranges: Seq[String]): Seq[String] = xs match {
    case y +: ys if (y - max <= 1) => reduce(ys, min, y, ranges)
    case y +: ys                   => reduce(ys, y, y, ranges :+ rangeToString(min, max))
    case Seq()                     => ranges :+ rangeToString(min, max)
}

def output(xs: Array[Int]) = reduce(xs, xs.head, xs.head, Vector())//.toArray

Что вы можете протестировать:

println(output(Array(108,109,110,112,114,115,116,118)))
  // Vector(108-110, 112, 114-116, 118)

По сути, это хвостовая рекурсивная функция, т.е. вы берете свои «переменные» в качестве входных данных, а затем вызываете себя с обновленными «переменными» в каждом цикле. Итак, здесь xs — ваш массив, min и max — целые числа, используемые для отслеживания наименьших и наибольших чисел на данный момент, а ranges — это выходная последовательность строк, которая добавляется при необходимости.

Первый шаблон (y — первый элемент, а ys — остальная часть последовательности, потому что так работает экстрактор +:) сопоставляется, если есть хотя бы один элемент (ys может быть пустым списком) и следует из предыдущего максимум.

Во-вторых, если он не следует, и необходимо сбросить минимум и добавить завершенный диапазон к выходным данным.

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

Интернет-карма указывает на любого, кто может решить, как устранить дублирование ranges :+ rangeToString(min, max)!

person Luigi Plinge    schedule 03.12.2017

вот решение:

def combineConsecutive(s: String): Seq[String] = {
  val ints: List[Int] = s.split(',').map(_.toInt).toList.reverse

  ints
    .drop(1)
    .foldLeft(List(List(ints.head)))((acc, e) => if ((acc.head.head - e) <= 1)
      (e :: acc.head) :: acc.tail
    else
      List(e) :: acc)
    .map(group => if (group.size > 1) group.min + "-" + group.max else group.head.toString)
}


val in = "108,109,110,112,114,115,116,118"

val result = combineConsecutive(in)

println(result) // List(108-110, 112, 114-116, 118)

}

В этом решении частично используется код из этого вопроса: Группировка элементов списка путем сравнения их с соседями

person Raphael Roth    schedule 02.12.2017
comment
Спасибо @raphael за решение. Будучи новичком в этом деле, я никогда не думал, что это можно сделать так просто. Спасибо еще раз! - person Daksh Agarwal; 02.12.2017