我想通过几个动态标准对哈希数组进行排序。假设我有这个数组
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"
}
]
我非常感谢您对此提供的帮助!
答案 0 :(得分:3)
使用sort_by
按desc顺序对字符串进行排序非常具有挑战性,最好使用“较低级别”的sort方法,该方法使用<=>
运算符按指定的比较器进行排序。一个快速的解决方案看起来像:
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
该块必须实现a和b之间的比较,并且当a跟随b时返回-1,如果a和b相等则返回0,或者如果b跟随a则返回+1。
因此,我们遍历sort_settings
并使用<=>
比较指定的字段,该字段返回1
,0
或-1
。如果指定的顺序为desc
,我们将求反。如果比较器返回的值不为零,则无需继续迭代。
答案 1 :(得分:2)
忽略asc
/ desc
功能,并假定排序键以符号形式给出并采用以下格式:
sort_settings = [
:first_name,
:age,
]
您可以轻松做到:
persons.sort_by{|p| p.values_at(sort_settings)}
答案 2 :(得分:1)
代码
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
示例
persons
符合问题中的定义。
sort_settings = [{field: :first_name, order: "asc"}, {field: :age, order: "desc"}]
sort_by_settings(persons, sort_settings)
#=> [{:id=>2, :first_name=>"Alexia", :last_name=>"Reyes", :age=>70},
# {:id=>3, :first_name=>"Anthony", :last_name=>"Nelson", :age=>25},
# {:id=>1, :first_name=>"Bill", :last_name=>"Zamora", :age=>37}]
persons1 = persons + [{ id: 4, first_name: "Alexia", last_name: "Whoosit", age: 71 }]
sort_by_settings(persons1, sort_settings)
#=> [{:id=>4, :first_name=>"Alexia", :last_name=>"Whoosit", :age=>71},
# {:id=>2, :first_name=>"Alexia", :last_name=>"Reyes", :age=>70},
# {:id=>3, :first_name=>"Anthony", :last_name=>"Nelson", :age=>25},
# {:id=>1, :first_name=>"Bill", :last_name=>"Zamora", :age=>37}]
sort_settings1 = [{field: :first_name, order: "desc"}, {field: :age, order: "asc"}]
sort_by_settings(persons1, sort_settings1)
#=> [{:id=>1, :first_name=>"Bill", :last_name=>"Zamora", :age=>37},
# {:id=>3, :first_name=>"Anthony", :last_name=>"Nelson", :age=>25},
# {:id=>2, :first_name=>"Alexia", :last_name=>"Reyes", :age=>70},
# {:id=>4, :first_name=>"Alexia", :last_name=>"Whoosit", :age=>71}]
说明
在第一个示例的计算中,计算了以下中间值。
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]}
答案 3 :(得分:0)
您将需要一种方法,以配置为指导,将给定项目转换为排序键:
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
然后,您只需在sort_by中调用它即可:
persons.sort_by!{ |p| build_sort_key_for(p, configuration) }
让"desc"
为字符串工作本身就是一个挑战,因此留给读者(或一个单独的问题)。