Scala按属性对元组列表进行排序

3
我想知道如何以字母顺序排序List[(String, String)]中的第一个字符串,同时删除元组中第二个字符串的所有重复项。谢谢您的建议!

4
这段代码是使用Scala语言编写的,主要用于对键值对列表进行排序,按照键的升序排列。其中.sortBy(_._1)表示按照列表中元素的第一个值(即键)进行排序。 - om-nom-nom
3个回答

8
我们可以运行性能测试吗?
val items = List("a" -> 0, "b" -> 1, "c" -> 0, "d" -> 0, "e" -> 1)
items.groupBy(_._2).toList
     .sortBy(_._2.head._1)(new Ordering[String]() { 
         override def compare(x: String, y: String) = { -x.compareTo(y) } 
     })
     .map(e => (e._2.head._1 -> e._1))

结果:

List((b,1), (a,0))

2
自定义排序有什么好处?为什么不使用Ordering[String].reverse - om-nom-nom
谢谢!我最终只使用了默认排序,效果非常好。 - Jin
Jin可能希望按小写字母顺序排序字符串,例如-这就是答案中使用自定义顺序的原因。 - idonnie

1

对于好奇的人,评论中链接的答案仍然有效。

如果您已经拥有TreeMap,则groupBy将构建TreeMaps。该行类似于idonnie的行,只是不必再次排序。

TreeMap(data: _*) groupBy (_._2) map (p => (p._2.head._1, p._1))

我不确定为什么它不能直接从一对列表构建TreeMap。我正在与感冒药雾中的迷雾作斗争。但当人们发布这些问题时,总是很有趣。

package object distinctby {
  import scala.collection.IterableLike
  import scala.collection.generic.CanBuildFrom
  import scala.collection.mutable.{ Set => MutSet }

  implicit class RichCollection[A, Repr](val xs: IterableLike[A, Repr]) extends AnyVal {
    def distinctBy[B, That](f: A => B)(implicit cbf: CanBuildFrom[Repr, A, That]) = {
      val builder = cbf(xs.repr)
      val i = xs.iterator
      val set = MutSet[B]()
      while (i.hasNext) {
        val o = i.next
        val b = f(o)
        if (!set(b)) {
          set += b
          builder += o
        }
      }
      builder.result
    }
  }
}

package distinctby {
  import scala.collection.generic.CanBuildFrom
  import scala.collection.immutable.TreeMap
  object Test extends App {
    val data = List("eins"->"one","un"->"one","zwei"->"two","deux"->"two")
    println(data)
    println(data distinctBy (_._2))
    println(TreeMap((data map (_.swap)): _*))
    println(TreeMap((data.reverse map (_.swap)): _*))
    // groupBy yields a TreeMap of TreeMap, so head is the lexly first
    println(TreeMap(data: _*) groupBy (_._2) map (p => (p._2.head._1, p._1)))
    locally {
      class CBF[A,B](implicit ord: Ordering[A]) 
        extends CanBuildFrom[List[(A, B)], (A,B), TreeMap[A,B]] {
        def apply(from: List[(A,B)]) = TreeMap.newBuilder[A, B](ord)
        def apply() = TreeMap.newBuilder[A, B]
      }
      implicit def cbf[A,B](implicit ord: Ordering[A]) = new CBF[A,B]
      println(data.distinctBy[String, TreeMap[String, String]](_._2)(cbf[String, String]).toSeq)
    }
  }
}

List((eins,one), (un,one), (zwei,two), (deux,two))
List((eins,one), (zwei,two))
Map(one -> un, two -> deux)
Map(one -> eins, two -> zwei)
Map(eins -> one, deux -> two)
ArrayBuffer((eins,one), (zwei,two))

1
我会使用列表的sorted方法来对元组进行默认排序。您还可以使用groupBy(如idonnie所指出的那样)来获取每个对中第二个项目的不同值。这是一个小例子:
scala> val tuples = scala.util.Random.shuffle("abcd".permutations.map{_.splitAt(2)}.toList) // generate some test data
tuples: List[(String, String)] = List((cb,ad), (dc,ab), (ba,dc), (bd,ca), (cb,da), (ca,db), (cd,ba), (cd,ab), (db,ca), (ba,cd), (ac,db), (ac,bd), (ab,cd), (ad,cb), (ca,bd), (bd,ac), (ad,bc), (db,ac), (da,bc), (da,cb), (bc,da), (dc,ba), (ab,dc), (bc,ad))

scala> tuples.sorted.groupBy(_._2).values.map(_.head).toList.sorted
res0: List[(String, String)] = List((ab,cd), (ab,dc), (ac,bd), (ac,db), (ad,bc), (ad,cb), (bc,ad), (bc,da), (bd,ac), (bd,ca), (cd,ab), (cd,ba))

在我看来,你没有指定一种具体的方法来选择要保留哪个副本似乎有点奇怪... 这应该会保留第一个(按第一个字符串排序),这也是我假设你想要的。如果你不在意保留哪个,那么可以删除对 sorted 的第一个调用。如果将第一个调用改为 sortBy(_._1),这可能也会稍微更有效率一些,但我不太了解你的具体应用程序是否会有任何实际差别。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接