Scala разделить список на несколько списков - PullRequest
0 голосов
/ 19 ноября 2018

Мне нужно разделить список на несколько списков на основе суммы размера элемента.Так что это то, что я написал для достижения этого.Я уверен, что будет лучший способ написать то же самое в Scala.Любая помощь приветствуется.

вот мой код ...

case class cs(name: String, size: Int)
val cl: List[cs] = List(cs("abc1", 3), cs("abc2", 2), cs("abc3", 1), cs("abc4", 2), cs("abc5", 2), cs("abc6", 1), cs("abc7", 5))

def splitBy(l: List[cs], chunkSize: Int = 5): List[List[cs]] = {

  var s = 0
  var tcl: List[cs] = List[cs]()
  var mcl: List[List[cs]] = List[List[cs]]()

  l.foreach{ e => {
    s = s + e.size
    if (s > chunkSize) {
      mcl = tcl +: mcl
      s = e.size
      tcl = Nil
    }
    tcl = e +: tcl
    println("TSize: " + tcl.size + " Elem: " + e + "Sum: " + s)
  }
  }

  mcl

}

println(splitBy(cl,5))

Ответы [ 2 ]

0 голосов
/ 19 ноября 2018

Вы можете использовать foldLeft, затем map, если порядок не имеет значения, вы можете избавиться от reverse s:

case class cs(name: String, size: Int)
val cl: List[cs] = List(cs("abc1", 3), cs("abc2", 2), cs("abc3", 1), cs("abc4", 2), cs("abc5", 2), cs("abc6", 1), cs("abc7", 5))

def splitBy(l: List[cs], chunkSize: Int = 5): List[List[cs]] =
  l.foldLeft(List[(List[cs],Int)]()) {
    case (accum @ (_, size: Int) :: _, next: cs) if size + next.size > chunkSize =>
      (next :: Nil, next.size) :: accum
    case ((chunk, size: Int) :: rest, next: cs) =>
      (next :: chunk, size + next.size) :: rest
    case (Nil, next: cs) =>
      (next :: Nil, next.size) :: Nil
  }.map {
    case (chunk, size: Int) =>
      chunk.reverse
  }.reverse

splitBy(cl, 5) foreach println

Вывод:

List(cs(abc1,3), cs(abc2,2))
List(cs(abc3,1), cs(abc4,2), cs(abc5,2))
List(cs(abc6,1))
List(cs(abc7,5))

Я изначально неверно истолковал вопрос, чтобы обозначить, что вы хотите, чтобы куски составляли по крайней мере chunkSize.Это был мой старый ответ, который я оставлю здесь для потомков, используя scanLeft, за которым следует collect:

case class cs(name: String, size: Int)
val cl: List[cs] = List(cs("abc1", 3), cs("abc2", 2), cs("abc3", 1), cs("abc4", 2), cs("abc5", 2), cs("abc6", 1), cs("abc7", 5))

def splitBy(l: List[cs], chunkSize: Int = 5): List[List[cs]] =
  l.scanLeft(List[cs]() -> 0) {
    case ((_, size: Int), next: cs) if size >= chunkSize =>
      (next :: Nil, next.size)
    case ((chunk, size: Int), next: cs) =>
      (next :: chunk, size + next.size)
  }.collect {
    case (chunk, size: Int) if size >= chunkSize =>
      chunk.reverse
  }

splitBy(cl, 5) foreach println

Вывод:

List(cs(abc1,3), cs(abc2,2))
List(cs(abc3,1), cs(abc4,2), cs(abc5,2))
List(cs(abc6,1), cs(abc7,5))
0 голосов
/ 19 ноября 2018
  @tailrec
  def splitBy(l: List[cs], chunkSize: Int, innerAcc: List[cs] = List.empty[cs], outerAcc: List[List[cs]] = List.empty[List[cs]]): List[List[cs]] = (l, innerAcc) match {
    case (Nil, Nil) => outerAcc
    case (Nil, x) => x :: outerAcc
    case (x :: xs, a) =>
      if (x.size > chunkSize) {
        // Assumed we are ignoring anything > chunkSize
        splitBy(xs, chunkSize, a, outerAcc)
      }
      // You may want to pass sum forwards for efficiency rather than recalculate every time... 
      else if(x.size + a.map(_.size).sum > chunkSize) {
        splitBy(xs, chunkSize, List(x), a :: outerAcc)
      }
      else {
        splitBy(xs, chunkSize, x :: a, outerAcc)
      }
  }

Выход

List(List(cs(abc7,5)), List(cs(abc6,1)), List(cs(abc5,2), cs(abc4,2), cs(abc3,1)), List(cs(abc2,2), cs(abc1,3)))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...