Scala将列表拆分为多个列表

时间:2018-11-19 13:20:32

标签: scala list

我需要根据一个元素的大小之和将一个List分成多个列表。这就是我为实现这一目标而写的内容。 我相信会有更好的方法在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 :(得分:1)

  @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)))

答案 1 :(得分:0)

您可以使用foldLeft,然后使用map,如果顺序无关紧要,则可以摆脱reverse

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))