总结:
我编写了一个非常高效的函数,它返回 List.distinct 和 List ,其中包含多次出现的每个元素以及出现重复元素的索引。
详情:
如果您需要更多关于重复项本身的信息,就像我所做的那样,我已经编写了一个更通用的函数,它只迭代一次 List(因为排序很重要)并返回一个由原始 @987654327 组成的 Tuple2 @deduped(删除第一个之后的所有重复项;即与调用distinct 相同)和第二个List 显示每个重复项以及它在原始List 中出现的Int 索引。
我已经基于general performance characteristics of the Scala collections实现了两次功能; 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)) //contains is linear
(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))
如果您只想要重复项,您只需 map 穿过 dupeAndIndexes 并调用 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
对于非常大的Lists,考虑使用这个更高效的版本(它使用额外的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) //contains is effectively constant
(
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_._。