繁体   English   中英

Scala-根据出现的次数修改列表中的字符串

[英]Scala - modify strings in a list based on their number of occurences

另一个Scala新手问题,因为我没有获得如何以功能方式实现此目标(主要来自脚本语言背景):

我有一个字符串列表:

val food-list = List("banana-name", "orange-name", "orange-num", "orange-name", "orange-num", "grape-name")

并且在重复的地方,我想在字符串中添加一个递增的数字,并在类似于输入列表的列表中获取它,如下所示:

List("banana-name", "orange1-name", "orange1-num", "orange2-name", "orange2-num", "grape-name")

我将它们归类为:

val freqs = list.groupBy(identity).mapValues(v => List.range(1, v.length + 1))

这给了我:

Map(orange-num -> List(1, 2), banana-name -> List(1), grape-name -> List(1), orange-name -> List(1, 2))

列表的顺序很重要 (应该按照food-list的原始顺序),因此我知道此时使用Map是有问题的。 我觉得最接近解决方案的是:

food-list.map{l =>

    if (freqs(l).length > 1){

            freqs(l).map(n => 
                           l.split("-")(0) + n.toString + "-" + l.split("-")(1))

    } else {
        l
    }
}

当然,这给了我一个靠不住的输出,因为我从字值映射频率列表freqs

List(banana-name, List(orange1-name, orange2-name), List(orange1-num, orange2-num), List(orange1-name, orange2-name), List(orange1-num, orange2-num), grape-name)

如何以Scala fp的方式完成操作而又不笨拙地进行循环和计数?

如果索引很重要,则有时最好使用zipWithIndex明确地跟踪它们(非常类似于Python的enumerate ):

food-list.zipWithIndex.groupBy(_._1).values.toList.flatMap{
  //if only one entry in this group, don't change the values
  //x is actually a tuple, could write case (str, idx) :: Nil => (str, idx) :: Nil
  case x :: Nil => x :: Nil
  //case where there are duplicate strings
  case xs => xs.zipWithIndex.map {
    //idx is index in the original list, n is index in the new list i.e. count
    case ((str, idx), n) =>
      //destructuring assignment, like python's (fruit, suffix) = ...
      val Array(fruit, suffix) = str.split("-")
      //string interpolation, returning a tuple
      (s"$fruit${n+1}-$suffix", idx)
  }
//We now have our list of (string, index) pairs;
//sort them and map to a list of just strings
}.sortBy(_._2).map(_._1)

高效而简单:

val food = List("banana-name", "orange-name", "orange-num", 
             "orange-name", "orange-num", "grape-name")

def replaceName(s: String, n: Int) = {
  val tokens = s.split("-")
  tokens(0) + n + "-" + tokens(1)
}

val indicesMap = scala.collection.mutable.HashMap.empty[String, Int]
val res = food.map { name =>
  {
    val n = indicesMap.getOrElse(name, 1)
    indicesMap += (name -> (n + 1))
    replaceName(name, n)
  }
}

是尝试为您提供foldLeft期望:

foodList.foldLeft((List[String](), Map[String, Int]()))//initial value
    ((a/*accumulator, list, map*/, v/*value from the list*/)=>
         if (a._2.isDefinedAt(v))//already seen
             (s"$v+${a._2(v)}" :: a._1, a._2.updated(v, a._2(v) + 1))
         else
             (v::a._1, a._2.updated(v, 1)))
    ._1/*select the list*/.reverse/*because we created in the opposite order*/

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM