Code
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
Examples
persons is as defined in the question.
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}]
Explanation
In the calculations for the first example, the following intermediate values were computed.
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]}
descpart may not be as simple as you think. Just for that functionality, the code could be pretty much complicated.fieldvalues in yoursort_settingsas symbols rather than strings given that the keys in your original hashes are symbols.#reverseto get the opposite. Update: actually that will only help when sorting by multiple fields with different orders.