摘要:
我编写了一个非常高效的函数,它返回List.distinct
和一个由每个重复出现的元素及其重复出现的索引组成的List
。
详细信息:
如果您需要更多关于重复项本身的信息,就像我一样,我编写了一个更通用的函数,该函数在一个List
上进行迭代(因为排序很重要),仅执行一次,并返回一个Tuple2
,其中包含原始List
去重后的结果(所有第一次之后的重复项都被删除;即与调用distinct
相同),以及第二个List
,显示每个重复项及其在原始List
中出现的Int
索引。
我基于Scala集合的一般性能特征实现了两次函数; filterDupesL
(其中L表示线性)和filterDupesEc
(其中Ec表示有效常数)。
这是“线性”函数:
def filterDupesL[A](items: List[A]): (List[A], List[(A, Int)]) = {
def recursive(
remaining: List[A]
, index: Int =
0
, accumulator: (List[A], List[(A, Int)]) =
(Nil, Nil)): (List[A], List[(A, Int)]
) =
if (remaining.isEmpty)
accumulator
else
recursive(
remaining.tail
, index + 1
, if (accumulator._1.contains(remaining.head))
(accumulator._1, (remaining.head, index) :: accumulator._2)
else
(remaining.head :: accumulator._1, accumulator._2)
)
val (distinct, dupes) = recursive(items)
(distinct.reverse, dupes.reverse)
}
以下是一个例子,可能会让它更加直观。给定这个字符串值的列表:
val withDupes =
List("a.b", "a.c", "b.a", "b.b", "a.c", "c.a", "a.c", "d.b", "a.b")
...然后执行以下操作:
val (deduped, dupeAndIndexs) =
filterDupesL(withDupes)
...结果如下:
deduped: List[String] = List(a.b, a.c, b.a, b.b, c.a, d.b)
dupeAndIndexs: List[(String, Int)] = List((a.c,4), (a.c,6), (a.b,8))
如果你只想要重复的内容,你可以简单地在dupeAndIndexes
上使用map
并调用distinct
:
val dupesOnly =
dupeAndIndexs.map(_._1).distinct
...或者在单个调用中完成所有操作:
val dupesOnly =
filterDupesL(withDupes)._2.map(_._1).distinct
如果更喜欢使用 Set
,可以跳过 distinct
并调用 toSet
方法。
val dupesOnly2 =
dupeAndIndexs.map(_._1).toSet
...或者在一个调用中完成所有操作:
val dupesOnly2 =
filterDupesL(withDupes)._2.map(_._1).toSet
对于非常大的 List
,考虑使用这个更高效的版本(它使用一个额外的 Set
在有效恒定时间内更改 contains
检查):
这是“有效恒定”函数:
def filterDupesEc[A](items: List[A]): (List[A], List[(A, Int)]) = {
def recursive(
remaining: List[A]
, index: Int =
0
, seenAs: Set[A] =
Set()
, accumulator: (List[A], List[(A, Int)]) =
(Nil, Nil)): (List[A], List[(A, Int)]
) =
if (remaining.isEmpty)
accumulator
else {
val (isInSeenAs, seenAsNext) = {
val isInSeenA =
seenAs.contains(remaining.head)
(
isInSeenA
, if (!isInSeenA)
seenAs + remaining.head
else
seenAs
)
}
recursive(
remaining.tail
, index + 1
, seenAsNext
, if (isInSeenAs)
(accumulator._1, (remaining.head, index) :: accumulator._2)
else
(remaining.head :: accumulator._1, accumulator._2)
)
}
val (distinct, dupes) = recursive(items)
(distinct.reverse, dupes.reverse)
}
上述两个函数都是我开源的Scala库ScalaOlio中filterDupes
函数的改编版。它位于org.scalaolio.collection.immutable.List_._
。
List(_,_,_*)
可以被替换为_::_::_
。是否更清晰取决于具体情况。此外,ys.size > 1
可以被替换为ys.lengthCompare(1) > 0
,以避免遍历整个重复的列表来查找大小。 - The Archetypal Pauldup.groupBy(identity).map(t => (t._1, t._2.size))
。结果为:Map(101 -> 2, 5 -> 2, 1 -> 3, 6 -> 1, 102 -> 1, 2 -> 1, 3 -> 1, 4 -> 1, 100 -> 1)
。 - Alicase (x, List(_,_,_*))
没有匹配任何内容,因为我的输入Seq
是一个缓冲区,因此我必须使用case (x, Buffer(_,_,_*))
进行匹配...真的很烦人去追踪。所以我不太喜欢这种方法,它太依赖于实现。 - Etienne Neveudup.groupBy(identity).collect { case (x, items) if items.size > 1 => x }
这样也可以工作,并且更易于阅读,我个人认为。 - Alvaro Mendez