[英]“natural” sort an array of hashes in Ruby
There are workable answers for sorting an array of hashes and for natural sorting , but what is the best way to do both at once? 对于散列哈希数组和自然排序有可行的答案,但是同时进行这两种操作的最佳方法是什么?
my_array = [ {"id":"some-server-1","foo":"bar"},{"id":"some-server-2","foo":"bat"},{"id":"some-server-10","foo":"baz"} ]
I would like to sort on "id" such that the final ordering is: 我想对“id”进行排序,以便最终的排序是:
some-server-1
some-server-2
some-server-10
I feel like there must be a clever and efficient way to do this, though personally I don't need to break any speed records and will only be sorting a few hundred items. 我觉得必须有一个聪明有效的方法来做到这一点,虽然我个人不需要打破任何速度记录,只会排序几百项。 Can I implement a comparison function in sort_by?
我可以在sort_by中实现比较功能吗?
First of all, your my_array
is JavaScript/JSON so I'll assume that you really have this: 首先,你的
my_array
是JavaScript / JSON,所以我假设你真的有这个:
my_array = [
{"id" => "some-server-1", "foo" => "bar"},
{"id" => "some-server-2", "foo" => "bat"},
{"id" => "some-server-10", "foo" => "baz"}
]
Then you just need to sort_by
the numeric suffix of the 'id'
values: 然后你只需
sort_by
'id'
值的数字后缀sort_by
:
my_array.sort_by { |e| e['id'].sub(/^some-server-/, '').to_i }
If the "some-server-" prefixes aren't always "some-server-" then you could try something like this: 如果“some-server-”前缀并不总是“some-server-”那么你可以尝试这样的事情:
my_array.sort_by { |e| e['id'].scan(/\D+|\d+/).map { |x| x =~ /\d/ ? x.to_i : x } }
That would split the 'id'
values into numeric and non-numeric pieces, convert the numeric pieces to integers, and then compare the mixed string/integers arrays using the Array <=>
operator (which compares component-wise); 这会将
'id'
值拆分为数字和非数字片段,将数字片段转换为整数,然后使用Array <=>
运算符 (比较组件)比较混合字符串/整数数组; this will work as long as the numeric and non-numeric components always match up. 只要数字和非数字组件始终匹配,这将起作用。 This approach would handle this:
这种方法可以解决这个问题
my_array = [
{"id" => "some-server-1", "foo" => "bar"},
{"id" => "xxx-10", "foo" => "baz"}
]
but not this: 但不是这个:
my_array = [
{"id" => "11-pancakes-23", "foo" => "baz"},
{"id" => "some-server-1", "foo" => "bar"}
]
If you need to handle this last case then you'd need to compare the arrays entry-by-entry by hand and adjust the comparison based on what you have. 如果您需要处理这最后一种情况,那么您需要手动逐个比较数组,并根据您拥有的内容调整比较。 You could still get some of the advantages of the
sort_by
Schwartzian Transform with something like this (not very well tested code): 您仍然可以使用类似的东西(不是经过良好测试的代码)获得
sort_by
Schwartzian 变换的一些优点:
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']) }
The basic idea here is to push the comparison noise off to another class to keep the sort_by
from degenerating into an incomprehensible mess. 这里的基本思想是将比较噪声推迟到另一个类,以使
sort_by
不会退化为难以理解的混乱。 Then we use the same scanning as before to break the strings into pieces and implement the array <=>
comparator by hand. 然后我们使用与以前相同的扫描将字符串分成几部分并手动实现数组
<=>
比较器。 If we have two things of the same class then we let that class's <=>
deal with it otherwise we force both components to String and compare them as such. 如果我们有两个相同类的东西,那么我们让该类的
<=>
处理它,否则我们强制将两个组件都串起来并比较它们。 And we only care about the first non-0 result. 我们只关心第一个非0结果。
@mu gives a more than adequate answer for my case, but I also figured out the syntax for introducing arbitrary comparisons: @mu为我的案例提供了一个足够的答案,但我也想出了引入任意比较的语法:
def compare_ids(a,b)
# Whatever code you want here
# Return -1, 0, or 1
end
sorted_array = my_array.sort { |a,b| compare_ids(a["id"],b["id"] }
我想如果你在id
字段上排序,你可以试试这个:
my_array.sort { |a,b| a["id"].to_i <=> b["id"].to_i }
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.