计算Scala中元组间隔列表中列表中数字发生的次数

时间:2019-01-18 14:19:33

标签: scala tuples accumulator

说我有一个元组列表:

 val ranges= List((1,4), (5,8), (9,10))

和数字列表

val nums = List(2,2,3,7,8,9)

我想从一个范围内的元组创建一个映射,以使num中给定数字落入该元组的区间的次数。

输出:

Map ((1,4) -> 3, (5,8) -> 2, (9,10) -> 1)

在Scala中解决它的最好方法是什么

我一直在尝试用于循环并保持计数器,但是没有达到要求。任何帮助将不胜感激。

最好的问候

2 个答案:

答案 0 :(得分:5)

类似这样的东西:

val ranges = List((1, 4), (5, 8), (9, 10))
val nums = List(2, 2, 3, 7, 8, 9)

val occurences = ranges.map { case (l, r) => nums.count((l to r) contains _) }
val map = (ranges zip occurences).toMap

println(map) // Map((1,4) -> 3, (5,8) -> 2, (9,10) -> 1)

基本上,它首先计算出现次数[3,2,1]。从那里很容易构造地图。它计算出现次数的方式是:

  • 浏览范围列表
  • 将每个范围转换为该范围的出现次数,方法如下:
    • 在该范围内,列表nums中包含多少个数字?

编辑:不确定为什么这个答案会被否决,也许我以某种方式误解了这个问题。

答案 1 :(得分:3)

这是一种有效的单程解决方案:

ranges
  .map(r => r -> nums.count(n => n >= r._1 && n <= r._2))
  .toMap

这避免了创建数字列表然后在单独的步骤中将它们与范围压缩在一起的开销。

这是一个使用了更多Scala功能但有点花哨的版本:

(for {
  r <- ranges
  range = r._1 to r._2
} yield r -> nums.count(range.contains)
).toMap

效率也较低,因为contains必须允许步长值的范围,因此更加复杂。


这是一个更加高效的版本,避免了任何临时数据结构:

val result: Map[(Int, Int), Int] =
  ranges.map(r => r -> nums.count(n => n >= r._1 && n <= r._2))(collection.breakOut)

如果您对此不熟悉,请参阅此explanation of breakOut。使用breakOut意味着map调用将直接构建Map而不是创建必须使用{{1}转换为List的{​​{1}} }。