scala集合:映射列表并携带一些状态?

时间:2018-04-08 05:29:24

标签: scala collections

我似乎一直遇到这个问题。我想修改列表中的一些元素,但是我需要保持一些状态,因此map不起作用。

以下是一个例子:

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)

我想更改任何重复项的名称。所以我想最终得到这个:

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并更新它的函数来实现它的工作。递归前的哈希值。哪个工作正常,但看起来有点难看。

但是我一直认为应该有办法去收集课程。

以下是我不喜欢的其余解决方案:

  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)

这给了我想要的东西

res2: List[String] = List(a1, b1, c1, d, e1, f, b2, c2, e2, b3, a2)

我觉得我想要“mapWithState”,我可以传递一些东西。就像Fold-ish一样。也许它存在,我还没有找到它?

由于

------- UPDATE ---------

@Aluan Haddad的评论向我指出了这个方向。这破坏了秩序,这对我的情况很好。但是“状态”由zipWithIndex承载。我正在寻找一个更一般的情况,其中状态需要在每个元素处进行一些计算。但对于这个简单的案例,我喜欢它:

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)

2 个答案:

答案 0 :(得分:2)

棘手的部分是"d""f"元素无法修改。

这就是我想出的。它更简洁,代码更明智,但确实涉及多次遍历。

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

答案 1 :(得分:0)

一个简单但很慢的版本

import pickle
class MyBaseException:
    def __init__(self, msg):
        self.msg = msg

class Error(MyBaseException):
    def __init__(self):
        super().__init__("Some error message")

c = Error()
a = pickle.dumps(c)
b = pickle.loads(a)

下一个版本更长,更不漂亮,不起作用,但在处理很长列表的不太可能的情况下应该更快。

l1.zipWithIndex.map{ case (elem, i) =>
  if (l1.count(_ == elem) == 1) {
    elem
  } else {
    val n = {l1.take(i+1).count(_ == elem)}
    s"$elem$n"
  }
}

如果您想将计数应用于每个元素而不仅仅是非唯一元素,则代码会更容易。

def makeUniq(in: Seq[String]): Seq[String] = {
  // Count occurrence of each element
  val m = mutable.Map.empty[String, Int]

  for (elem <- in) {
    m.update(elem, m.getOrElseUpdate(elem, 0) + 1)
  }

  // Remove elements with a single occurrence
  val dupes = m.filter(_._2 > 1)

  // Apply numbering to duplicate elements
  in.reverse.map(e => {
    val idx = dupes.get(e) match {
      case Some(i) =>
        dupes.update(e, i - 1)
        i.toString
      case _ =>
        ""
    }

    s"$e$idx"
    }).reverse
  }