Scala实现为重复字符串添加重复次数后缀并保持顺序
Scala风格实现重复元素添加计数后缀
需求很明确:给定一个String类型的List,生成新List,其中重复出现的元素(区分大小写)会在后面添加.加上重复次数作为后缀,第一个出现的元素保持原样,同时严格保留原列表的顺序。比如输入List("a","a","a","b","b","c","C"),要输出List("a","a.1","a.2","b","b.1","c","C")。
用Scala的foldLeft可以很优雅地实现这个需求,它能在遍历过程中携带状态(这里就是每个元素的出现次数),完全替代传统的循环+计数Map的写法:
val input = List("a","a","a","b","b","c","C") val result = input.foldLeft((scala.collection.mutable.ListBuffer[String](), Map.empty[String, Int])) { case ((buffer, countMap), elem) => val currentCount = countMap.getOrElse(elem, 0) if (currentCount == 0) { buffer += elem (buffer, countMap + (elem -> 1)) } else { buffer += s"$elem.$currentCount" (buffer, countMap + (elem -> (currentCount + 1))) } }._1.toList // 输出结果: List("a", "a.1", "a.2", "b", "b.1", "c", "C")
代码说明
- foldLeft的初始状态:用一个元组保存两个值,一个是用来动态添加结果的
ListBuffer(比List更适合频繁追加操作),另一个是记录每个元素出现次数的Map,初始为空。 - 遍历逻辑:
- 对每个元素,先从计数Map里拿到它当前的出现次数,默认是0(第一次出现)。
- 如果是第一次出现,直接把元素加入结果缓冲区,同时更新Map中该元素的计数为1。
- 如果不是第一次出现,就生成带后缀的字符串加入缓冲区,然后把Map中该元素的计数加1。
- 最终转换:把
ListBuffer转成不可变的List,符合Scala的惯用风格。
如果想全程用不可变集合(避免使用ListBuffer),也可以把结果改成不可变List,但频繁追加的话性能会稍差一点,写法如下:
val input = List("a","a","a","b","b","c","C") val result = input.foldLeft((List.empty[String], Map.empty[String, Int])) { case ((resList, countMap), elem) => val currentCount = countMap.getOrElse(elem, 0) val newElem = if (currentCount == 0) elem else s"$elem.$currentCount" (resList :+ newElem, countMap + (elem -> (currentCount + 1))) }._1
这个版本完全用不可变结构,更符合函数式编程的纯粹性,但因为List的:+操作是O(n)的,数据量大的时候推荐用第一个版本的ListBuffer。
内容的提问来源于stack exchange,提问作者DQd
相关产品推荐
相关产品推荐

