从列表中获取所有重复记录而没有二次时间复杂度?

时间:2018-02-12 07:51:55

标签: scala performance time-complexity user-defined-functions scala-collections

以下是我的列表,其中包含列名:country,gender&年龄。

scala> funList
res1: List[(String, String, String)] = List((india,M,15), (usa,F,25), (australia,M,35), (kenya,M,55), (russia,M,75), (china,T,95), (england,F,65), (germany,F,25), (finland,M,45), (australia,F,35))

我的目标是找到具有(国家,年龄)组合的重复记录。请注意,我只想获取所有重复记录并忽略其他记录。并且列表还应包含具有重复记录的其他列值。

输出应该是这样的:

australia,M,35
australia,F,35

如果没有groupBy操作且没有n * square复杂度,那将会很好。 GroupBy很好,除非它弄乱了我的输出。

2 个答案:

答案 0 :(得分:2)

没有groupBy()。不确定复杂性。

val keys = funList.map{case (a,b,c) => (a,c)}  //isolate elements of interest
val dups = keys diff keys.distinct             //find the duplicates
funList.filter{case (a,b,c) => dups.contains((a,c))}
//res0: List[(String, String, String)] = List((australia,M,35), (australia,F,35))

答案 1 :(得分:1)

不确定groupBy混淆输出的可能性是什么意思。您可以按照以下方式使用它,并且您将获得您正在寻找的重复项列表:

// input
val items = List(("india","M",15), ("usa","F",25), ("australia","M",35), ("kenya","M",55), ("russia","M",75), ("china","T",95), ("england","F",65), ("germany","F",25), ("finland","M",45), ("australia","F",35))

items.
  groupBy { case (nation, _, age) => nation -> age }. // group by relevant items
  filter(_._2.length > 1).                            // keep only duplicates
  flatMap(_._2)                                       // get them and flatten the result

或者您可能有兴趣使用groupBy作为您自己的函数的基础,该函数通过键来存储值并使用某个谓词过滤结果,如下所示:

implicit class FilterGroups[A, CC[X] <: Iterable[X]](self: CC[A]) {
  import scala.collection.mutable
  import scala.collection.mutable.Builder
  import scala.collection.generic.CanBuildFrom
  def filterGroups[K, That](f: A => K)(p: CC[A] => Boolean)(implicit bfs: CanBuildFrom[CC[A], A, CC[A]], bf: CanBuildFrom[CC[A], A, That]): That = {
    val m = mutable.Map.empty[K, Builder[A, CC[A]]]
    for (elem <- self) {
      val key = f(elem)
      val bldr = m.getOrElseUpdate(key, bfs())
      bldr += elem
    }
    val b = bf()
    for {
      (_, v) <- m
      group = v.result if p(group)
      elem <- group
    } b += elem
    b.result
  }
}

然后您按如下方式调用它:

// bucket by the first function, filter by the second one
items.filterGroups(tuple => (tuple._1, tuple._3))(_.length > 1)

并且,如上所述,取回所需的项目列表:

List((australia,M,35), (australia,F,35))

替代解决方案的唯一主要优势是输出类型与输入相同,而使用groupBy强制您的结果类型为Iterable[(String, String, Int)]。不确定你是否可能通过弄乱输出来表示这一点。

无论哪种方式,我都说时间复杂度实际上是线性的(我们必须将一个传递给桶,一个传递给过滤器,但我们仍然可以用big-O表示法去掉常量)。这当然意味着空间复杂性也与集合大小绑定(因为我们打破了原始结果)。

最后一点说明:在测量尺寸时,您可能不想使用列表,因为它的复杂性是线性的。我的解决方案和使用groupBy的解决方案都使用相同类型的原始集合的构建器,因此您可能希望使用Vector或其他集合与 O(1)用于计算length

但是正确的答案可能就是使用groupBy ,这对任何其他Scala开发人员来说都比较简单明了(尽管您可能也希望在迭代中使用惰性视图防止对数据进行不必要的双重传递。)