【问题标题】:Filter the list based on tuple element根据元组元素过滤列表
【发布时间】:2015-11-12 22:37:02
【问题描述】:

我有一个列表[(Int,Int)]。

例如

val a = List((1,2),(2,3),(1,4),(2,4),(5,6),(4,5),(1,8))

我想过滤这个列表,以便如果几个元组具有相同的第一个元素(_1 中的值相同),则只保留第一个元组。

所以这里的预期答案是:

val ans=List((1,2),(2,3),(5,6),(4,5))

由于(1,2) 的第一个元素是1(1,4)(1,8) 也是如此,所以我们只保留第一个元素((1,2))并忽略其他元素((1,4)和@987654330 @)。

我该怎么做?

【问题讨论】:

    标签: scala


    【解决方案1】:
    a.groupBy(_._1).values.collect{case x::xs=>x}
    

    按照@Régis Jean-Gilles 的建议进行编辑,

    a.groupBy(_._1).values.map(_.head)
    

    编辑,保留顺序

    a.foldLeft(Map.empty[Int,(Int,Int)].empty){
        case (r,(x,y))=> if(r.contains(x)) r else r+(x->(x,y))
    }.values
    

    【讨论】:

    • 此解决方案不保留顺序。不过我不知道这对你是否重要。
    • a.groupBy(_._1).values.map(_.head) 也可以工作(这里head 保证是安全的,因为groupBy 返回的每个列表都必须至少有一个元素)。虽然使用groupBy你会失去订单。也就是说,最终不仅元组的顺序可能不正确,而且我什至不确定groupBy 是否保证在每个列表中保持元素的顺序(在这种情况下,你甚至不能保证保留 first 匹配元组,但只有 一个 匹配元组)
    • 订单对我来说并不重要。但是如果我需要订单,有什么解决方案吗?是的 groupBy 保持列表中的元素按顺序排列。
    • @Regis Jean-Gilles,来自 groupBy 的来源,它看起来很安全。但是,map 和使用 head 似乎是正确的方法。
    • 当我谈到保证时,我的意思是它是一份合同。毫无疑问,当前实现保留了每个列表中的顺序,但它只是一个可以在未来版本中更改的实现细节吗?
    【解决方案2】:

    ListMap 让我们既能消除重复键又能保持顺序:

    scala> val a= List((1,2),(2,3),(1,4),(2,4),(5,6),(4,5),(1,8))
    a: List[(Int, Int)] = List((1,2), (2,3), (1,4), (2,4), (5,6), (4,5), (1,8))
    
    scala> collection.immutable.ListMap(a.reverse: _*).toList.reverse
    res0: List[(Int, Int)] = List((1,2), (2,3), (5,6), (4,5))
    

    【讨论】:

    • 酷,我不知道 ListMap。
    • ListMap 的合约(而不是当前的实现)是否保证订单?
    • 是的,这就是 ListMap 的全部思想。它将在 Scala 2.11.8 中记录为:github.com/scala/scala/pull/4788。我看到 ListMap 文档没有指定如何处理重复键;我相信我们也会接受将其添加到文档中的拉取请求。
    • 啊,很酷(/me 将 ListMap 添加到他的技巧箱中)。有趣的是看到我以前的 cmets 提到:)
    【解决方案3】:
    scala> val a = List((1,2),(2,3),(1,4),(2,4),(5,6),(4,5),(1,8))
    a: List[(Int, Int)] = List((1,2), (2,3), (1,4), (2,4), (5,6), (4,5), (1,8))
    scala> a.reverse.toMap.toList
    res7: List[(Int, Int)] = List((1,2), (4,5), (5,6), (2,3))
    

    不保留顺序,但相当简洁。

    【讨论】:

    • 查看我对保留订单版本的回答
    【解决方案4】:

    这保留了顺序并保证在发现重复项时只保留第一个,但这不是一个非常有效的算法。对于长列表可能不是一个好主意。

    scala> val a= List((1,2),(2,3),(1,4),(2,4),(5,6),(4,5),(1,8))
    a: List[(Int, Int)] = List((1,2), (2,3), (1,4), (2,4), (5,6), (4,5), (1,8))
    
    scala> a.filter(x => a.filter(_._1 == x._1).head == x)
    res22: List[(Int, Int)] = List((1,2), (2,3), (5,6), (4,5))
    

    【讨论】:

    • 感谢@jwvh .. 它完美解决了这个问题。
    • head 不安全,永远不应使用 - stackoverflow.com/questions/23183935/…
    • @KevinMeredith,在这种情况下,head 安全的。内部filter 将始终返回至少一个元素。
    • head is what is known as a partial function: there are certain inputs for which head will crash. - cis.upenn.edu/~cis194/spring13/lectures/03-rec-poly.html
    • @Kevin Meredith:您缺少的是,根据groupBy 的定义,这里的列表保证不为空。所以是的,在此处调用head 确实是安全的(如果它在运行时失败,我们在groupBy 中有一个必须修复的错误)。
    【解决方案5】:

    您要求的几乎是.distinct,对“重复”进行了不同的测试。看看标准库是怎么做的.distinct

     /** Builds a new $coll from this $coll without any duplicate elements.
       *  $willNotTerminateInf
       *
       *  @return  A new $coll which contains the first occurrence of every element of this $coll.
       */
      def distinct: Repr = {
        val b = newBuilder
        val seen = mutable.HashSet[A]()
        for (x <- this) {
          if (!seen(x)) {
            b += x
            seen += x
          }
        }
        b.result()
      }
    

    我们可以做一些不太通用的事情,但大体相同:

      import scala.collection.mutable.{ListBuffer, HashSet}
    
      def distinctByFirst[A,B](xs:List[(A, B)]) = {
        val b = new ListBuffer[(A,B)]
        val seen = HashSet[A]()
        for (x <- xs) {
          if (!seen(x._1)) {
            b += x
            seen += x._1
          }
        }
        b.result()
      }
    
      distinctByFirst(a)
      //> res0: List[(Int, Int)] = List((1,2), (2,3), (5,6), (4,5))
    

    【讨论】:

      猜你喜欢
      • 2018-07-14
      • 1970-01-01
      • 1970-01-01
      • 2019-08-03
      • 2013-09-17
      • 1970-01-01
      • 2019-10-22
      • 1970-01-01
      相关资源
      最近更新 更多