【问题标题】:How to take the first distinct (until the moment) elements of a list?如何获取列表的第一个不同(直到此刻)元素?
【发布时间】:2014-01-27 17:42:53
【问题描述】:

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

改写: 是否有任何标准库(集合)方法或它们的简单组合来获取列表的第一个不同元素?

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.

【问题讨论】:

  • @Vidya,添加了更具体的示例。
  • 标准库可以有一个适用于目前看到的所有元素的 takeWhile。但是,实际的 takeWhile 仅用于测试目前唯一的元素。
  • @flavian:问题已得到纠正。这个例子是对的。

标签: scala collections


【解决方案1】:

如果你希望它是快速的,那么

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

会成功的。 (额外的大括号可防止泄漏温度值hs。)

【讨论】:

  • 计算成本不是必需的,实际上它是相当可读的。
  • ...而且很有创意。
  • 这真是太棒了。
【解决方案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 以获取实时结果。

【讨论】:

  • 没那么容易:)。 val ex = Seq(3, 5, 4, 1, 5, 7, 1, 5) 应该返回 3,5,4,1,但返回 3,5,4
  • val ex3 = Seq(3, 5, 4, 1, 5, 3, 7, 1, 5) //3, 5, 4, 1, 5, 3, 7, 1, 5。已经有答案了,反正tnx。
【解决方案3】:

有趣的问题。这是一个替代方案。首先,让我们获取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

【讨论】:

  • 似乎正确,谢谢。我将发布我现在找到的替代方案。
【解决方案4】:

雷克斯的变种(虽然我更喜欢他的……)

这个功能贯穿始终,使用鲜为人知的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}

如果需要,可以将生成的迭代器转回序列,但这也适用于输入和输出上的迭代器。

【讨论】:

    【解决方案5】:

    这个问题比我要找的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)
    

    【讨论】:

    • 任何有助于使其更惯用的帮助将不胜感激。
    • takeWhileConditionOverAllAlreadyTraversedItems 函数值得在 SO 中提出另一个问题。
    猜你喜欢
    • 2016-12-31
    • 2012-08-21
    • 2019-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-31
    相关资源
    最近更新 更多