Вот эффективное однопроходное решение:
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)
См. это объяснение breakOut
, если вы не знакомы с ним , Использование breakOut
означает, что вызов map
будет создавать Map
напрямую, а не создавать List
, который должен быть преобразован в Map
с использованием toMap
.
Примерно так:
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
содержится в этом диапазоне? РЕДАКТИРОВАТЬ: Не уверен, почему этот ответ получает отрицательный голос, возможно, я как-то неправильно понял вопрос .