我需要根据一个元素的大小之和将一个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))
答案 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))