让我们从一个整数序列开始,如:
val seq = List(1,2,3,4,5,6,9,10,11,14,15,16,18)
我想获得一系列表示连续集的对,例如:
val ranges = List(1,6,9,11,14,16,18,18)
替代格式Seq[(Int,Int)]
也可以接受:
val ranges = List((1,6),(9,11),(14,16),(18,18))
说明:
- 范围1..6
和11..16
中的整数位于seq
- 整数18
位于seq
,但没有后继者或前任,因此在18,18
中显示为ranges
请注意,单元素序列应始终作为对报告,例如:
val seq = List(18, 19, 21)
应该给出结果:
val ranges = List(18,19,21,21)
或者,如果您更喜欢 Tuple2样式:
val ranges = List((18,19),(21,21))
我希望有一个函数从ranges
派生seq
;解决方案(由同事提供)是:
def toRanges(a: Seq[Int]): Seq[Int] = {
val min = a.map(x => (x, a contains x - 1)).filter(!_._2).map(_._1)
val max = a.map(x => (x, a contains x + 1)).filter(!_._2).map(_._1)
return (min ++ max).sorted
}
确实很优雅,但由于contains
的使用,我不确定效率。
任何人都可以在效率或优雅方面提供更好的解决方案吗?
谢谢!
答案 0 :(得分:3)
如果输入是有序的(或者您愿意先对其进行排序),您可以使用foldLeft
一次性完成这一步骤(嗯,两次使用reverse
,但是这是一个使用列表的工件,如果你愿意放弃一些优雅,可以避免这样做:
seq.foldLeft[List[(Int, Int)]](Nil) {
case ((a, b) :: rest, i) if i == b + 1 => (a, i) :: rest
case (acc, i) => (i, i) :: acc
}.reverse
在这种情况下,我们提供以下内容:
res0: List[(Int, Int)] = List((1,6), (9,11), (14,16), (18,18))
对于每个元素,我们检查它是否是我们添加的最后一个范围结束的后继。如果是,我们将替换该范围内的结束。如果不是,我们开始新的范围。
答案 1 :(得分:2)
或许这样的事情:
(None +: seq.toStream.map(x => Some(x)) :+ None).sliding(2).flatMap {
case Seq(None, Some(b)) => List(b)
case Seq(Some(a), None) => List(a)
case Seq(Some(a), Some(b)) if b - a == 1 => Nil
case Seq(Some(a), Some(b)) => List(a,b)
}.toList