如何在列表中查找重复项?

时间:2014-07-14 04:30:09

标签: list scala scala-collections

我有一个未排序的整数列表,我想找到那些有重复的元素。

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 } )

有没有更简单的方法来解决这个问题?

5 个答案:

答案 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.distinctList两个元素,这些元素出现不止一次,元素重复的索引也出现了。

<强>详细信息: 如果您需要更多关于重复项本身的信息,就像我一样,我编写了一个更通用的函数,它在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库ScalaOliofilterDupes函数的改编版。它位于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)
}
相关问题