我正在尝试在重复的元素上生成序列号。值更改时应重置为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?
是否有更好的方法来获得上述输出?
答案 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