对于sorting an array of hashes和natural sorting都有可行的答案,但同时做到这两个目的的最佳方法是什么?
my_array = [ {"id":"some-server-1","foo":"bar"},{"id":"some-server-2","foo":"bat"},{"id":"some-server-10","foo":"baz"} ]
我想按“id”排序,以便最终排序为:
some-server-1
some-server-2
some-server-10
我觉得必须有一个聪明和有效的方法来做到这一点,虽然就我个人而言,我不需要打破任何速度记录,只会排序几百个项目。我能按排序方式实现比较函数吗?
最佳答案
首先,您的my_array
是javascript/json,所以我假设您真的有:
my_array = [
{"id" => "some-server-1", "foo" => "bar"},
{"id" => "some-server-2", "foo" => "bat"},
{"id" => "some-server-10", "foo" => "baz"}
]
然后您只需要
sort_by
值的数字后缀:my_array.sort_by { |e| e['id'].sub(/^some-server-/, '').to_i }
如果“some server”(某些服务器)前缀并不总是“some server”(某些服务器),则可以尝试以下操作:
my_array.sort_by { |e| e['id'].scan(/\D+|\d+/).map { |x| x =~ /\d/ ? x.to_i : x } }
这将把
'id'
值拆分为数值和非数值部分,将数值部分转换为整数,然后使用数组'id'
operator比较混合字符串/整数数组(按组件比较);只要数值和非数值部分始终匹配,这将起作用。这种方法可以处理以下问题:my_array = [
{"id" => "some-server-1", "foo" => "bar"},
{"id" => "xxx-10", "foo" => "baz"}
]
但不是这个:
my_array = [
{"id" => "11-pancakes-23", "foo" => "baz"},
{"id" => "some-server-1", "foo" => "bar"}
]
如果需要处理最后一种情况,则需要手动逐个比较数组条目,并根据所拥有的内容调整比较。您仍然可以通过这样的方式(测试不太好的代码)获得
<=>
SchwartzianTransform的一些优点:class NaturalCmp
include Comparable
attr_accessor :chunks
def initialize(s)
@chunks = s.scan(/\D+|\d+/).map { |x| x =~ /\d/ ? x.to_i : x }
end
def <=>(other)
i = 0
@chunks.inject(0) do |cmp, e|
oe = other.chunks[i]
i += 1
if(cmp == 0)
cmp = e.class == oe.class \
? e <=> oe \
: e.to_s <=> oe.to_s
end
cmp
end
end
end
my_array.sort_by { |e| NaturalCmp.new(e['id']) }
这里的基本思想是将比较噪声推到另一个类中,以防止
sort_by
退化为不可理解的混乱。然后,我们使用与之前相同的扫描方式将字符串分割成块,并手动实现数组比较器。如果我们有同一个类的两个东西,那么我们让该类的sort_by
处理它,否则我们将强制两个组件字符串化并进行比较。我们只关心第一个非0结果。