scala collections:映射列表并携带一些状态? [英] scala collections : map a list and carry some state?
问题描述
我似乎一直都在遇到这个问题。我想修改列表中的某些元素,但是在执行操作时需要保持一些状态,因此map无法正常工作。
I seem to run into this problem all the time. I want to modify some of the elements in a list, but I need to keep some state as I do it, so map doesn't work.
这里是一个示例:
scala> val l1 = List("a","b","c","d","e","f","b","c","e","b","a")
l1: List[String] = List(a, b, c, d, e, f, b, c, e, b, a)
我想更改任何重复项的名称。所以我想结束这个:
I want to change the name of any duplicates. so I want to end up with this:
List(a1, b1, c1, d, e1, f, b2, c2, e2, b3, a2)
获取骗子很容易:
scala> val d = l1.diff(l1.distinct).distinct
d: List[String] = List(b, c, e, a)
现在我被卡住了。我通过将d转换为带有计数的HashMap并编写函数以遍历l1并对其进行更新来使其工作。递归之前的哈希值。
Now I'm stuck. I made it work by converting d to a HashMap w/ a count, and writing a function to iterate over l1 and update it & the hash before recursing. Which works fine, but looks kinda ugly to me.
但是我一直认为应该有一种方法可以处理集合类。
But I've always thought there should be a way to do w/ the collection classes.
这是我不喜欢的解决方案的其余部分:
Here is the rest of my solution which I don't like :
val m = d.map( _ -> 1).toMap
def makeIt(ms: Map[String, Int], ol: Iterator[String], res: List[String]=List[String]()) :List[String] = {
if( !ol.hasNext) return res
val no = ol.next()
val (p,nm) = ms.get(no) match {
case Some(v) => (s"$no$v", ms.updated(no,v+1))
case None => (no,ms)
}
makeIt(nm,ol,res :+ p)
}
makeIt(m,l1.iterator)
哪个给了我我想要的东西
Which gives me what I want
res2: List[String] = List(a1, b1, c1, d, e1, f, b2, c2, e2, b3, a2)
我觉得我想在 mapWithState中传递一些信息。像折纸一样。也许它存在而我还没有找到?
I feel like I want "mapWithState" where I can just pass something along. Like Fold-ish. Maybe it exists and I just haven't found it yet?
谢谢
------ -UPDATE ---------
-------UPDATE---------
@Aluan Haddad的评论向我指出了这一方向。这会破坏秩序,这对我的情况很好。但是状态由zipWithIndex携带。我正在寻找一种更一般的情况,即状态需要在每个元素上进行一些计算。但是对于这种简单的情况,我喜欢它:
@Aluan Haddad's comment pointed me in this direction. Which destroys order, which is fine for my case. But the "state" is carried by zipWithIndex. I'm looking for a more general case where the state would require some computation at each element. But for this simple case I like it :
l1.groupBy(x=>x).values.flatMap( v =>{
if( v.length <= 1 ) v else {
v.zipWithIndex.map{ case (s,i) => s"$s${i+1}"}
}
})
res7: Iterable[String] = List(e1, e2, f, a1, a2, b1, b2, b3, c1, c2, d)
推荐答案
棘手的部分是 d
和 f
元素没有任何修改。
The tricky part is that the "d"
and "f"
elements get no modification.
这就是我来的跟上。
This is what I came up with. It's a bit more concise, code wise, but does involve multiple traversals.
val l1: List[String] = List("a","b","c","d","e","f","b","c","e","b","a")
l1.reverse.tails.foldLeft(List[String]()){
case (res, Nil) => res
case (res, hd::tl) =>
val count = tl.count(_ == hd)
if (count > 0) s"$hd${count+1}" +: res
else if (res.contains(hd+2)) (hd+1) +: res
else hd +: res
}
//res0: List[String] = List(a1, b1, c1, d, e1, f, b2, c2, e2, b3, a2)
通过使用 tails
,每个元素 hd
,都能看到未来, tl
,以及过去的 res
。
By using tails
, each element, hd
, is able to see the future, tl
, and the past, res
.
这篇关于scala collections:映射列表并携带一些状态?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!