在 Scala 中扩展 Seq.sortBy

发布于 2024-10-17 22:06:42 字数 845 浏览 3 评论 0原文

假设我有一份名单。

case class Name(val first: String, val last: String)

val names = Name("c", "B") :: Name("b", "a") :: Name("a", "B") :: Nil

如果我现在想按姓氏对该列表进行排序(如果这还不够,则按名字排序),这很容易完成。

names.sortBy(n => (n.last, n.first))
// List[Name] = List(Name(a,B), Name(c,B), Name(b,a))

但是,如果我想根据其他字符串排序规则对该列表进行排序,该怎么办?

不幸的是,以下方法不起作用:

val o  = new Ordering[String]{ def compare(x: String, y: String) = collator.compare(x, y) }
names.sortBy(n => (n.last, n.first))(o)
// error: type mismatch;
// found   : java.lang.Object with Ordering[String]
// required: Ordering[(String, String)]
//   names.sortBy(n => (n.last, n.first))(o)

有什么方法可以让我更改顺序,而不必编写具有多个 ifelse< 的显式 sortWith 方法/code> 分支以便处理所有情况?

Say I have a list of names.

case class Name(val first: String, val last: String)

val names = Name("c", "B") :: Name("b", "a") :: Name("a", "B") :: Nil

If I now want to sort that list by last name (and if that is not enough, by first name), it is easily done.

names.sortBy(n => (n.last, n.first))
// List[Name] = List(Name(a,B), Name(c,B), Name(b,a))

But what, if I‘d like to sort this list based on some other collation for strings?

Unfortunately, the following does not work:

val o  = new Ordering[String]{ def compare(x: String, y: String) = collator.compare(x, y) }
names.sortBy(n => (n.last, n.first))(o)
// error: type mismatch;
// found   : java.lang.Object with Ordering[String]
// required: Ordering[(String, String)]
//   names.sortBy(n => (n.last, n.first))(o)

is there any way that allow me to change the ordering without having to write an explicit sortWith method with multiple ifelse branches in order to deal with all cases?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

忆梦 2024-10-24 22:06:42

好吧,这几乎成功了:

names.sorted(o.on((n: Name) => n.last + n.first))

另一方面,您也可以这样做:

implicit val o  = new Ordering[String]{ def compare(x: String, y: String) = collator.compare(x, y) }
names.sortBy(n => (n.last, n.first))

这个本地定义的隐式将优先于 Ordering 对象上定义的隐式。

Well, this almost does the trick:

names.sorted(o.on((n: Name) => n.last + n.first))

On the other hand, you can do this as well:

implicit val o  = new Ordering[String]{ def compare(x: String, y: String) = collator.compare(x, y) }
names.sortBy(n => (n.last, n.first))

This locally defined implicit will take precedence over the one defined on the Ordering object.

自由如风 2024-10-24 22:06:42

一种解决方案是扩展隐式使用的 Tuple2 排序。不幸的是,这意味着在代码中写出Tuple2

names.sortBy(n => (n.second, n.first))(Ordering.Tuple2(o, o))

One solution is to extend the otherwise implicitly used Tuple2 ordering. Unfortunately, this means writing out Tuple2 in the code.

names.sortBy(n => (n.second, n.first))(Ordering.Tuple2(o, o))
混吃等死 2024-10-24 22:06:42

我不能 100% 确定您认为 collat​​or 应该具有哪些方法。

但是,如果您在案例类上定义排序,则具有最大的灵活性:

val o = new Ordering[Name]{
  def compare(a: Name, b: Name) =
    3*math.signum(collator.compare(a.last,b.last)) +
    math.signum(collator.compare(a.first,b.first))
}
names.sorted(o)

但您也可以提供从字符串排序到名称排序的隐式转换:

def ostring2oname(os: Ordering[String]) = new Ordering[Name] {
  def compare(a: Name, b: Name) = 
    3*math.signum(os.compare(a.last,b.last)) + math.signum(os.compare(a.first,b.first))
}

然后您可以使用任何字符串排序对名称进行排序:

def oo = new Ordering[String] {
  def compare(x: String, y: String) = x.length compare y.length
}
val morenames = List("rat","fish","octopus")

scala> morenames.sorted(oo)
res1: List[java.lang.String] = List(rat, fish, octopus)

编辑:一个方便的技巧,以防万一,如果你想按 N 个东西排序并且你已经在使用比较,你可以将每个东西乘以 3^k (第一个到顺序乘以最大的3) 的幂并相加。


如果您的比较非常耗时,您可以轻松添加级联比较:

class CascadeCompare(i: Int) {
  def tiebreak(j: => Int) = if (i!=0) i else j
}
implicit def break_ties(i: Int) = new CascadeCompare(i)

然后

def ostring2oname(os: Ordering[String]) = new Ordering[Name] {
  def compare(a: Name, b: Name) =
    os.compare(a.last,b.last) tiebreak os.compare(a.first,b.first)
}

(只需小心嵌套它们 x tiebreak ( y tiebreak ( z tiebreak w ) ) ) 这样您就不会连续进行多次隐式转换)。

(如果您确实需要快速比较,那么您应该手动将其全部写出来,或者将顺序打包在一个数组中并使用 while 循环。我假设您对性能并不那么渴望。)

I'm not 100% sure what methods you think collator should have.

But you have the most flexibility if you define the ordering on the case class:

val o = new Ordering[Name]{
  def compare(a: Name, b: Name) =
    3*math.signum(collator.compare(a.last,b.last)) +
    math.signum(collator.compare(a.first,b.first))
}
names.sorted(o)

but you can also provide an implicit conversion from a string ordering to a name ordering:

def ostring2oname(os: Ordering[String]) = new Ordering[Name] {
  def compare(a: Name, b: Name) = 
    3*math.signum(os.compare(a.last,b.last)) + math.signum(os.compare(a.first,b.first))
}

and then you can use any String ordering to sort Names:

def oo = new Ordering[String] {
  def compare(x: String, y: String) = x.length compare y.length
}
val morenames = List("rat","fish","octopus")

scala> morenames.sorted(oo)
res1: List[java.lang.String] = List(rat, fish, octopus)

Edit: A handy trick, in case it wasn't apparent, is that if you want to order by N things and you're already using compare, you can just multiply each thing by 3^k (with the first-to-order being multiplied by the largest power of 3) and add.


If your comparisons are very time-consuming, you can easily add a cascading compare:

class CascadeCompare(i: Int) {
  def tiebreak(j: => Int) = if (i!=0) i else j
}
implicit def break_ties(i: Int) = new CascadeCompare(i)

and then

def ostring2oname(os: Ordering[String]) = new Ordering[Name] {
  def compare(a: Name, b: Name) =
    os.compare(a.last,b.last) tiebreak os.compare(a.first,b.first)
}

(just be careful to nest them x tiebreak ( y tiebreak ( z tiebreak w ) ) ) so you don't do the implicit conversion a bunch of times in a row).

(If you really need fast compares, then you should write it all out by hand, or pack the orderings in an array and use a while loop. I'll assume you're not that desperate for performance.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文