任何简单的方法来链接两个比较< => Ruby中的运算符?

时间:2014-06-24 03:50:40

标签: ruby comparison comparison-operators

Ruby带有方便的<=>比较运算符,原生基元类型支持它们。我想知道是否有一种简单的方法将它们组合起来比较更复杂的对象,比如Structs。例如,给定

class Datum < Struct.new(:code, :volume); end

datum1 = Datum.new('B',10)
datum2 = Datum.new('A',10)
datum3 = Datum.new('C',11)

data = [datum1, datum2, datum3]

我想按datavolume进行排序,如果volume s相等,则按code排序。像

data.sort {|a,b| (a.volume <=> b.volume) ??? (a.code <=> b.code)}

我应该在???中添加什么?

我的解决方案是:

  • 避免重新计算<=>
  • 单线
  • 短; - )

3 个答案:

答案 0 :(得分:4)

对于上述简单案例,您可以使用sort_by

data.sort_by {|a| [a.volume, a.code] }
#=> [
#     #<struct Datum code="A", volume=10>,
#     #<struct Datum code="B", volume=10>,
#     #<struct Datum code="C", volume=11>
#   ]

如果您只按单个属性进行排序,则会更短:

data.sort_by(&:volume)
#=> [
#     #<struct Datum code="B", volume=10>,
#     #<struct Datum code="A", volume=10>,
#     #<struct Datum code="C", volume=11>
#   ]

其中&:volume使用Symbol#to_proc并且是proc {|a| a.volume }的简写(类似于lambda)。

如果您需要使其更复杂(即左右不同),可以将其扩展为sort的调用:

data.sort {|a,b| [a.volume, a.code] <=> [b.volume, b.code] }
#=> [
#     #<struct Datum code="A", volume=10>,
#     #<struct Datum code="B", volume=10>,
#     #<struct Datum code="C", volume=11>
#   ]

这一切都有效,因为<=>上定义的Array运算符完全符合您的需要,适用于任意级别。

答案 1 :(得分:2)

我认为你以错误的方式解决这个问题。为什么不为自己省去麻烦并在<=>添加Datum运营商?像这样:

class Datum < Struct.new(:code, :volume)
  def <=>(other)
    if(self.code < other.code)
      -1
    elsif(self.code > other.code)
      1
    elsif(self.volume < other.volume)
      -1
    elsif(self.volume > other.volume)
      1
    else
      0
    end
  end
end

然后你可以说data.sort并完成它。一旦您拥有<=>运算符,您就可以include Comparable并免费获得<<=,...运算符。

答案 2 :(得分:1)

Ruby链接比较器的方法是使用Numeric.nonzero?

data.sort {|a,b| (a.volume <=> b.volume).nonzero? || a.code <=> b.code}

Numeric.nonzero?的作用是什么?如果它不为零,则返回数字,否则返回nil。然后||运算符将处理其余部分,因为在布尔上下文中将nil视为false,因此nil || bb相同。 / p>

这就是说,在大多数情况下,使用Enumerable.sort_by时您会得到较短的代码,并返回一个数组以指示排序顺序:

data.sort_by {|a| [a.volume, a.code] }

颠倒排序顺序比使用比较器还容易:

data.sort {|a,b| (-(a.volume <=> b.volume)).nonzero? || a.code <=> b.code}
# vs.
data.sort_by {|a| [-a.volume, a.code] }

警告:请注意,如果比较器比较慢,sort_by is slower than sort in case the comparator is fastsort_by会更好。