我有一个未排序的整数列表,我想找到那些有重复的元素。
val dup = List(1,1,1,2,3,4,5,5,6,100,101,101,102)
我可以使用dup.distinct找到该组的不同元素,所以我写了如下答案。
val dup = List(1,1,1,2,3,4,5,5,6,100,101,101,102)
val distinct = dup.distinct
val elementsWithCounts = distinct.map( (a:Int) => (a, dup.count( (b:Int) => a == b )) )
val duplicatesRemoved = elementsWithCounts.filter( (pair: Pair[Int,Int]) => { pair._2 <= 1 } )
val withDuplicates = elementsWithCounts.filter( (pair: Pair[Int,Int]) => { pair._2 > 1 } )
有没有更简单的方法来解决这个问题?
答案 0 :(得分:51)
试试这个:
val dup = List(1,1,1,2,3,4,5,5,6,100,101,101,102)
dup.groupBy(identity).collect { case (x, List(_,_,_*)) => x }
groupBy
将每个不同的整数与其出现的列表相关联。 collect
基本上是map
,其中忽略了不匹配的元素。 case
后面的匹配模式将匹配与符合模式x
的列表相关联的整数List(_,_,_*)
,该列表包含至少两个元素,每个元素由下划线表示,因为我们不知道实际上需要存储这些值(这两个元素后面可以跟零个或多个元素:_*
)。
你也可以这样做:
dup.groupBy(identity).collect { case (x,ys) if ys.lengthCompare(1) > 0 => x }
它比您提供的方法快得多,因为它不必重复传递数据。
答案 1 :(得分:27)
派对有点晚了,但这是另一种方法:
dup.diff(dup.distinct).distinct
diff
为您提供了参数(dup.distinct
)之上的所有额外项目,这些项目是重复的。
答案 2 :(得分:2)
另一种方法是使用foldLeft
并以艰难的方式去做。
我们从两个空集开始。一个是我们至少看过一次的元素。另一个我们已经看过至少两次的元素(又名重复)。
我们遍历清单。当已经看到当前元素(seen(cur)
)时,它是重复的,因此被添加到duplicates
。否则,我们将其添加到seen
。
结果现在是第二个包含重复项的集合。
我们也可以将其写为通用方法。
def dups[T](list: List[T]) = list.foldLeft((Set.empty[T], Set.empty[T])){ case ((seen, duplicates), cur) =>
if(seen(cur)) (seen, duplicates + cur) else (seen + cur, duplicates)
}._2
val dup = List(1,1,1,2,3,4,5,5,6,100,101,101,102)
dups(dup) //Set(1,5,101)
答案 3 :(得分:2)
<强>要点:强>
我编写了一个非常有效的函数,它返回List.distinct
和List
两个元素,这些元素出现不止一次,元素重复的索引也出现了。
<强>详细信息:强>
如果您需要更多关于重复项本身的信息,就像我一样,我编写了一个更通用的函数,它在List
(因为排序很重要)中迭代一次并返回Tuple2
组成的原始List
重复数据删除(删除第一个之后的所有重复项;即与调用distinct
相同),第二个List
显示每个副本和Int
索引发生在原始List
内。
我已根据general performance characteristics of the Scala collections实施了两次该功能; filterDupesL
(其中L代表线性)和filterDupesEc
(其中Ec代表有效常数)。
这是&#34;线性&#34;功能:
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
对于非常大的List
,请考虑使用此更高效的版本(使用额外的Set
来有效地更改contains
签入时间):
这里是&#34;有效常数&#34;功能:
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_._
。
答案 4 :(得分:0)
我最喜欢的是
def hasDuplicates(in: List[Int]): Boolean = {
val sorted = in.sortWith((i, j) => i < j)
val zipped = sorted.tail.zip(sorted)
zipped.exists(p => p._1 == p._2)
}