Arrays 在Ruby中动态排序哈希数组
我想根据几个动态标准对哈希数组进行排序。假设我有这个数组Arrays 在Ruby中动态排序哈希数组,arrays,ruby,sorting,Arrays,Ruby,Sorting,我想根据几个动态标准对哈希数组进行排序。假设我有这个数组 persons = [ { id: 1, first_name: "Bill", last_name: "Zamora", age: 37 }, { id: 2, first_name: "Alexia", last_name: "Reyes", age: 70 }, { id: 3, first_name: "Anthony",
persons = [
{
id: 1,
first_name: "Bill",
last_name: "Zamora",
age: 37
},
{
id: 2,
first_name: "Alexia",
last_name: "Reyes",
age: 70
},
{
id: 3,
first_name: "Anthony",
last_name: "Nelson",
age: 25
}
]
我知道您可以使用以下代码轻松地按多个条件对数组进行排序
persons.sort_by!{ |p| [p[:age], p[:first_name]] }
但是,在本例中,数组排序所依据的字段的编号和顺序是硬编码的。在我的例子中,这是在运行时动态确定的。所以我不知道数组要按多少字段排序,也不知道哪些字段按哪个顺序排序
我正在寻找一个优雅的解决方案,使用我以前不知道的配置对象对数组进行排序。这种配置可能如下所示:
sort_settings = [
{
field: "first_name",
order: "asc"
},
{
field: "age",
order: "desc"
}
]
我非常感谢在这方面的任何帮助 您需要一种方法,以配置为指导,将给定项转换为排序键:
def build_sort_key_for(item, configuration)
configuration.map { |entry|
value = item[entry[:field].to_sym]
value = -value if entry[:order] == "desc" # this will only work on numeric values
value
}
end
那你就把它叫做你的分类吧:
persons.sort_by!{ |p| build_sort_key_for(p, configuration) }
使
“desc”
用于字符串本身是一个挑战,因此留给读者(或一个单独的问题)。使用排序依据按desc顺序对字符串进行排序非常具有挑战性,最好使用“低级”方法,该方法使用
运算符按指定的比较器进行排序。此问题的快速解决方案如下所示:
persons.sort do |a, b|
comparator = 0
sort_settings.each do |s|
a_field = a[s[:field].to_sym]
b_field = b[s[:field].to_sym]
comparator = a_field <=> b_field
comparator = -comparator if s[:order] == "desc"
break unless comparator == 0
end
comparator
end
persons.sort do | a,b|
比较器=0
排序设置。每个do|
a_field=a[s[:field]。to_sym]
b_field=b[s[:field]。to_sym]
比较器=a_字段b_字段
比较器=-比较器如果s[:顺序]=“描述”
除非比较器==0,否则中断
结束
比较器
结束
块必须实现a和b之间的比较,当a跟在b后面时返回-1,当a和b相等时返回0,或者如果b跟在a后面时返回+1
因此,我们迭代排序设置
并使用
比较指定的字段,它返回1
、0
或-1
。如果指定的顺序是desc
,我们将反转该值。如果comparator返回的值不同于零,则无需继续迭代。忽略asc
/desc
功能,并假设排序键以符号形式给出,且采用以下格式:
sort_settings = [
:first_name,
:age,
]
您只需执行以下操作:
persons.sort_by{|p| p.values_at(sort_settings)}
代码
def sort_by_settings(persons, sort_settings)
sort_mult_by_field = sort_settings.each_with_object({}) do |g,h|
h[g[:field]] = g[:order] == "asc" ? 1 : -1
end
longest_string_by_key = persons.each_with_object(Hash.new(0)) do |g,h|
g.each { |k,v| h[k] = [h[k], g[k].size].max if sort_mult_by_field.key?(k) &&
v.is_a?(String) }
end
sort_by_arr = persons.each_with_object({}) do |g,h|
h[g] = sort_mult_by_field.each_with_object([]) do |(f,m),a|
gv = g[f]
a <<
case gv
when Integer
m * gv
when String
gv.chars.map { |c| m * c.ord }.concat([m * -256]*(longest_string_by_key[f]-gv.size))
else # rescue...
end
end
end
persons.sort_by { |g| sort_by_arr[g] }
end
解释
在第一个示例的计算中,计算了以下中间值
sort_mult_by_field
#=> {:first_name=>1, :age=>-1}
longest_string_by_key
#=> {:first_name=>7}
sort_by_arr
#=> {{:id=>1, :first_name=>"Bill", :last_name=>"Zamora", :age=>37}=>
# [[66, 105, 108, 108, -256, -256, -256], -37],
# {:id=>2, :first_name=>"Alexia", :last_name=>"Reyes", :age=>70}=>
# [[65, 108, 101, 120, 105, 97, -256], -70],
# {:id=>3, :first_name=>"Anthony", :last_name=>"Nelson", :age=>25}=>
# [[65, 110, 116, 104, 111, 110, 121], -25]}
desc
部分可能没有您想象的那么简单。就为了这个功能,,代码可能非常复杂。最好将排序设置中的字段
值作为符号,而不是字符串,因为原始哈希中的键是符号。对于顺序,您可以始终按一个方向排序,然后使用反向
获得相反的结果。更新:事实上,这只会在按不同顺序的多个字段排序时有所帮助。干得好!!只有使用“asc”
和“desc”
sort_mult_by_field
#=> {:first_name=>1, :age=>-1}
longest_string_by_key
#=> {:first_name=>7}
sort_by_arr
#=> {{:id=>1, :first_name=>"Bill", :last_name=>"Zamora", :age=>37}=>
# [[66, 105, 108, 108, -256, -256, -256], -37],
# {:id=>2, :first_name=>"Alexia", :last_name=>"Reyes", :age=>70}=>
# [[65, 108, 101, 120, 105, 97, -256], -70],
# {:id=>3, :first_name=>"Anthony", :last_name=>"Nelson", :age=>25}=>
# [[65, 110, 116, 104, 111, 110, 121], -25]}