[英]zipWithIndex for nested collections (release without mutable state)
There are some nested collections: 有一些嵌套集合:
val xs = List(
List("a","b"),
List("c"),
List("d", "e", "f"))
I want to create unique index for nested list's elements: 我想为嵌套列表的元素创建唯一索引:
val result = List(
List(("a", 0), ("b", 1)),
List(("c", 2)),
List(("d", 3), ("e", 4), ("f", 5)))
This is a bad solution (used mutable state): 这是一个糟糕的解决方案(使用可变状态):
val indexes:List[Int] = xs.flatten.zipWithIndex.map(_._2)
var idx = 0
val result = xs.map(_.map{ el =>
val copy = (el, indexes(idx))
idx = idx + 1
copy
})
How to release this task without mutable state? 如何在没有可变状态的情况下发布此任务?
Yet another variation: 另一种变化:
val index = Iterator.from(0)
for (sl <- xs) yield for (e <- sl) yield (e, index.next)
Quite neat, IMO, but iterators aren't pure functional, of course 相当简洁,IMO,但迭代器当然不是纯粹的功能
And a functional one, quite readable (to me, anyway) 功能性的,非常易读(无论如何)
val starts = xs.scanLeft(0)(_ + _.size)
(xs, starts, starts.tail).zipped map{ (sl, start, end) => sl zip ( start until end)}
Solution 1(using fold
): 解决方案1(使用fold
):
scala> xs.foldLeft((List[List[(String, Int)]](), 0)){
case ((r, i), x) => (r:+x.zip(Stream.from(i)), i+x.size)
}._1
res1: List[List[(String, Int)]] = List(List((a,0), (b,1)), List((c,2)), List((d,3), (e,4), (f,5)))
Solution 2(using recursion) 解决方案2(使用递归)
scala> def deepZip[A](ls: List[List[A]], i: Int = 0): List[List[(A, Int)]] = ls match {
| case Nil => Nil
| case x::xs => x.zip(Stream.from(i)) :: deepZip(xs, i+x.size)
| }
deepZip: [A](ls: List[List[A]], i: Int)List[List[(A, Int)]]
scala> deepZip(xs)
res2: List[List[(String, Int)]] = List(List((a,0), (b,1)), List((c,2)), List((d,3), (e,4), (f,5)))
Solution 3: 解决方案3:
scala> (xs, xs.map(_.size).scanLeft(0){ _+_ }).zipped map { (a, b) => a.zip(Stream.from(b)) }
res3: List[List[(String, Int)]] = List(List((a,0), (b,1)), List((c,2)), List((d,3), (e,4), (f,5)))
This is what I came up with: 这就是我想出的:
def iterate(someList: List[List[Int]], counter: Int = 0, acc: List[List[(Int, Int)]] = List()): List[List[(Int, Int)]] = someList match {
case head :: tail =>
val newCounter = counter + head.length
val sublist = head.zipWithIndex.map {
// add the counter to the index
case (v, i) => (v, i + counter)
}
// recurse
iterate(tail, newCounter, acc :+ sublist)
case _ => acc
}
scala> iterate(List(List(1,2), List(3,4)))
res3: List[List[(Int, Int)]] = List(List((1,0), (2,1)), List((3,2), (4,3)))
scala> iterate(List(List(1,2), List(3,4), List(5)))
res4: List[List[(Int, Int)]] = List(List((1,0), (2,1)), List((3,2), (4,3)), List((5,4)))
What this basically does is iterate a list of lists, zip with the index each sublist and add a counter value which takes into account all the previous list lengths, when the list is empty we return an accumulator. 这基本上做的是迭代列表列表,使用索引每个子列表压缩并添加计数器值,该值考虑所有先前的列表长度,当列表为空时,我们返回累加器。
But as I said, I wouldn't trade this with the mutable version. 但正如我所说,我不会用可变版本交换它。
You can always apply sufficient brute force to transform an imperative while loop to a functional foldLeft
: 您总是可以应用足够的强力来将命令式while循环转换为功能foldLeft
:
val xs = List(List("a", "b"), List("c"), List("d", "e", "f"))
def addIndeces(xs: List[List[String]]): List[List[(String, Int)]] = {
val outerLoopState = 0 -> Vector.empty[List[(String, Int)]]
val (finalCounter, finalResult) = xs.foldLeft(outerLoopState) {
case ((counter, result), sublist) =>
val innerLoopState = counter -> Vector.empty[(String, Int)]
val (newCounter, subResult) = sublist.foldLeft(innerLoopState) {
case ((counter, subResult), element) =>
(counter + 1, subResult :+ (element, counter))
}
(newCounter, result :+ subResult.toList)
}
finalResult.toList
}
// scala> addIndeces(xs)
// res0: List[List[(String, Int)]] = List(List((a,0), (b,1)), List((c,2)), List((d,3), (e,4), (f,5)))
I've used Vector
for the intermediate results to get more a efficient functional append
operation. 我已经使用Vector
作为中间结果来获得更有效的功能append
操作。 With List
I would have had to prepend and then reverse the intermediate results. 使用List
我将不得不先插入然后反转中间结果。
Plain not tail recursive... 平原不尾递归......
def zipFrom[A](start:Int)(l:List[A]):(List[(A,Int)],Int) = {
val end = start + l.length
val ys = l zip (start to end)
ys -> end
}
def zp[A](xs:List[List[A]],acc:Int):List[List[(A,Int)]] = xs match {
case Nil => Nil
case h :: t =>
val (l,e) = zipFrom(acc)(h)
l :: zp(t,e)
}
var index = 0
val lim = li.map {
case l @ List(_*) =>
val s = index
index += l.length
l.zip(s until index)
}
It works fast and simple 它工作快速而简单
variation on @Archetypal Paul using map instead of for 变形@Archetypal保罗使用地图代替
val ys = Iterator.from(0)
val zs = xs.map { _.map ((_,ys.next)) }
variation using zip & iterator & map 使用zip和迭代器和地图的变化
val ys = xs.flatten.zipWithIndex.iterator
val zs = xs.map { _.map ( x=> ys.next) }
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.