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

问题描述 投票:0回答:4

我正在尝试在重复的元素上生成序列号。值改变时应重置为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?

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

scala
4个回答
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
}

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) - 如果head string元素与当前dt元素相同
  • qazxsw poi - 在列表中添加一个带增量计数的新元素
  • (str,cnt+1) :: lst - head string元素与当前的case _元素不同
  • dt - 在列表中添加一个count = 1的新元素
  • (str,1) :: lst - 我们以相反的顺序构建结果,反转它

2
投票

希望这可以帮助。

.reverse

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

结果:

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)} )

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

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

结果

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

感谢@Leo,而不是索引,你可以使用带有zip的dtg: List[(String, Int)] = List((decimal,1), (decimal,2), (decimal,3), (date,1), (date,2), (string,1), (string,2)) 来提供相同的结果。

Stream from 1
© www.soinside.com 2019 - 2024. All rights reserved.