scala-当列表中重复的值更改时重置为1

时间:2019-04-25 19:07:36

标签: scala

我正在尝试在重复的元素上生成序列号。值更改时应重置为1,

val dt = List("date", "date", "decimal", "decimal", "decimal", "string", "string")
var t = 0
dt.sorted.map( x => {t=t+1; (x,t)} )

结果为

List((date,1), (date,2), (decimal,3), (decimal,4), (decimal,5), (string,6), (string,7))

但是我希望得到它

List((date,1), (date,2), (decimal,1), (decimal,2), (decimal,3), (string,1), (string,2))

当列表中的值更改时,如何将t的值更改为0?

是否有更好的方法来获得上述输出?

4 个答案:

答案 0 :(得分:3)

要重置计数器,您需要回顾.map()不能执行的上一个元素。

dt.foldLeft(List.empty[(String,Int)]){ case (lst,str) =>
    lst.headOption.fold((str,1)::Nil){
      case (`str`,cnt) => (str,cnt+1) :: lst
      case _           => (str,1)     :: lst
    }
}.reverse
//res0: List[(String, Int)] = List((date,1), (date,2), (decimal,1), (decimal,2), (decimal,3), (string,1), (string,2))

说明

  • foldLeft-从左到右一次考虑dt元素
  • List.empty[(String,Int)]-我们将建立一个List元组,从一个空列表开始
  • case (lst,str)-我们正在构建的列表以及String中当前的dt元素
  • lst.headOption-如果列表存在,请获取列表的头
  • fold((str,1)::Nil)-如果lst为空,则返回包含单个元素的新列表
  • case (str,cnt)-如果头字符串元素与当前的dt元素相同
  • (str,cnt+1) :: lst-将具有递增计数的新元素添加到列表中
  • case _-头字符串元素不同于当前的dt元素
  • (str,1) :: lst-向列表中添加一个新的元素,计数为1,
  • .reverse-我们以相反的顺序构建结果,将其反转

答案 1 :(得分:3)

用于此目的的最佳方法是scanLeft,它类似于foldLeft,但在每个步骤中都会发出一个值。代码如下:

val ds = dt.sorted
ds.tail.scanLeft((ds.head, 1)){
  case ((prev, n), cur)  if prev == cur => (cur, n+1)
  case (_, cur) => (cur, 1)
}

在每个步骤中,如果该值与前一个值相同,则将增加计数,否则将其重置为1。


如果列表具有单个元素,则此方法有效。尽管tail将是Nil,但scanLeft结果中的第一个元素始终是该方法的第一个参数。在这种情况下,它是(ds.head, 1)

如果列表为空,这将有效,因为ds.head将引发异常。可以先使用match来解决此问题:

ds match {
  case head :: tail =>
    tail.scanLeft((head, 1)) {
      case ((prev, n), cur) if prev == cur => (cur, n + 1)
      case (_, cur) => (cur, 1)
    }
  case _ => Nil
}

答案 2 :(得分:2)

希望这会有所帮助。

scala> val dt = List("date", "date", "decimal", "decimal", "decimal", "string", "string")
dt: List[String] = List(date, date, decimal, decimal, decimal, string, string)

scala> val dtset = dt.toSet
dtset: scala.collection.immutable.Set[String] = Set(date, decimal, string)

scala> dtset.map( x => dt.filter( y => y == x))
res41: scala.collection.immutable.Set[List[String]] = Set(List(date, date), List(decimal, decimal, decimal), List(string, string))

scala> dtset.map( x => dt.filter( y => y == x)).flatMap(a => a.zipWithIndex)
res42: scala.collection.immutable.Set[(String, Int)] = Set((string,0), (decimal,1), (decimal,0), (string,1), (date,0), (date,1), (decimal,2))

scala> dtset.map( x => dt.filter( y => y == x)).flatMap(a => a.zipWithIndex).toList
res43: List[(String, Int)] = List((string,0), (decimal,1), (decimal,0), (string,1), (date,0), (date,1), (decimal,2)) // sort this list to your needs

答案 3 :(得分:2)

通过再添加一个可变的字符串变量,下面的一个有效。

val dt = List("date", "date", "decimal", "decimal", "decimal", "string","string")
var t = 0
var s = ""
val dt_seq = dt.sorted.map( x => { t= if(s!=x) 1 else t+1;s=x; (x,t)} )

结果:

dt_seq: List[(String, Int)] = List((date,1), (date,2), (decimal,1), (decimal,2), (decimal,3), (string,1), (string,2))

另一种方法是使用groupBy(identity)并从地图值中获取索引

val dt = List("date", "date", "decimal", "decimal", "decimal", "string","string")
val dtg = dt.groupBy(identity).map( x => (x._2 zip x._2.indices.map(_+1)) ).flatten.toList

结果

dtg: List[(String, Int)] = List((decimal,1), (decimal,2), (decimal,3), (date,1), (date,2), (string,1), (string,2))

由于@Leo而不是索引,因此可以将Stream from 1与zip一起使用以产生相同的结果。

val dtg = dt.groupBy(identity).map( x => (x._2 zip (Stream from 1)) ).flatten.toList