如何获取列表的第一个不同(直到时刻)元素?

时间:2014-01-27 17:42:53

标签: scala collections

我确信有一种优雅/有趣的方式, 但我只能想到一个或多或少复杂的递归解决方案。

改述: 是否有任何标准的lib(集合)方法,也没有简单的组合来获取列表的第一个不同的元素?

scala> val s = Seq(3, 5, 4, 1, 5, 7, 1, 2)
s: Seq[Int] = List(3, 5, 4, 1, 5, 7, 1, 2)

scala> s.takeWhileDistinct //Would return Seq(3,5,4,1), it should preserve the original order and ignore posterior occurrences of distinct values like 7 and 2.

5 个答案:

答案 0 :(得分:7)

如果你想要快速,那么

{ val hs = scala.collection.mutable.HashSet[Int]()
  s.takeWhile{ hs.add } }

会做到这一点。 (额外的大括号可防止泄漏临时值hs。)

答案 1 :(得分:2)

这是一种最短的方法,最多为O(2logN)

implicit class ListOps[T](val s: Seq[T]) {
    def takeWhileDistinct: Seq[T] = {
      s.indexWhere(x => { s.count(x==) > 1 }) match {
        case ind if (ind > 0) => s.take(
          s.indexWhere(x => { s.count(x==) > 1 }, ind + 1) + ind).distinct
        case _ => s
      }
    }
  }

val ex = Seq(3, 5, 4, 5, 7, 1)
val ex2 = Seq(3, 5, 4, 1, 5, 7, 1, 5) 

println(ex.takeWhileDistinct.mkString(", ")) // 3, 4, 5
println(ex2.takeWhileDistinct.mkString(", ")) // 3, 4, 5, 1

查看here了解实时结果。

答案 2 :(得分:1)

有趣的问题。这是另一种选择。首先,让我们获取s的流,这样我们就可以避免不必要的工作(尽管开销很可能比保存的工作更大,但遗憾的是)。

val s = Seq(3, 5, 4, 5, 7, 1)
val ss = s.toStream

现在我们可以再次建立s,但是跟踪是否有重复,并在第一个停止:

val newS = ss.scanLeft(Seq[Int]() -> false) { 
  case ((seen, stop), current) => 
    if (stop || (seen contains current)) (seen, true) 
    else ((seen :+ current, false)) 
}

现在剩下的就是拿下最后一个元素而不重复,然后放下标志:

val noRepetitionsS = newS.takeWhile(!_._2).last._1

答案 3 :(得分:0)

问题比我正在寻找的std lib函数更简单(takeWhileConditionOverListOfAllAlreadyTraversedItems):

scala> val s = Seq(3, 5, 4, 1, 5, 7, 1, 2)
scala> s.zip(s.distinct).takeWhile{case(a,b)=>a==b}.map(_._1)
res20: Seq[Int] = List(3, 5, 4, 1)

答案 4 :(得分:0)

雷克斯的一个变种(虽然我更喜欢他......)

这一个功能始终正常,使用了很少见的scanLeft方法。

val prevs = xs.scanLeft(Set.empty[Int])(_ + _)
(xs zip prevs) takeWhile { case (x,prev) => !prev(x) } map {_._1}

更新

一个懒惰版本(使用迭代器,用于提高效率):

val prevs = xs.iterator.scanLeft(Set.empty[Int])(_ + _)
(prevs zip xs.iterator) takeWhile { case (prev,x) => !prev(x) } map {_._2}

如果需要,可以将生成的迭代器转回序列,但是这对输入和输出上的迭代器也很有效。