问题
I want to sort an array to sort in ascending or descending order. My array contains hash keys and the values I want to sort are float, integer and string (names). Names can be alphabetic or alphanumeric. I want to create a method that would handle all the sortings. It will take array, column name and sort order and return the rest of the array sorted. The following is the JSON output. My array contains the hash keys.
[
{
"sid": "101",
"snumber": "798798",
"name": "Anita 1",
"time": 1800,
"count": 32,
"hour": "",
"avg": 1
},
{
"sid": "160",
"snumber": "6546546",
"name": "Anita 22",
"time": 1300,
"count": 30,
"hour": "1",
"avg": 1
},
{
"sid": "120",
"snumber": "6546546",
"name": "Anita",
"time": 2300,
"count": 10,
"hour": "2",
"avg": 2
}
]
I tried many things but couldn't get anything right. Here's my method:
def self.sort_by_alphabets(arr, sortColumnName, sortOrder) # sortOrder a: ASC,d: DESC
column = sortColumnName.to_sym
return arr.sort_by { |h|
if sortColumnName == 'sid' || sortColumnName = 'snumber' || sortColumnName =='hour'
a = h[column].to_i
else
if h[column].is_a? String
a = h[column].to_s
type ='s'
elsif h[column].is_a? Float
a = h[column].to_f
type ='f'
else
a = h[column].to_i
type ='i'
end
if sortOrder == 'a'
a.downcase
else
a.upcase
end
}
end
Can anyone help me out?
An expected output is a sorted array. Say if I want to sort an array using sid
in ascending order then result array will sort by sid in the mentioned order, likewise for other keys. However, array can only be sorted in any one key at a time.
回答1:
Code
def sort_by_value(arr, key, ascending = true)
arr.sort_by do |h|
v = h[key]
raise TypeError, "#{v}.class => #{v.class} invalid" unless
(v.kind_of?(Numeric) && !v.is_a?(Complex)) || v.is_a?(String)
Float(v) rescue v.downcase
end.tap { |a| a.reverse! if ascending == false }
end
See Enumerable#sort_by, Object#kind_of? and Kernel#Float.
I have assumed that when a value is a string that represents a numeric value (other than a complex number), the sort is to be done on the associated number. Float(v)
attempts to convert the string v
to a float. If successful it returns float; else it raises an exception that is rescued (in line), causing v.downcase
to be returned.
Examples
arr = [
{ "name"=>"Anita 1", "time"=>18, "wt"=>2.31, "inbr"=> "79", "fnbr"=>"-2.31" },
{ "name"=>"Anita 22", "time"=>13, "wt"=>-12.4, "inbr"=> "654", "fnbr"=>"12.4" },
{ "name"=>"bubba", "time"=>23, "wt"=>12.84, "inbr"=>"-654", "fnbr"=>"-1284e-2" }
]
sort_by_value(arr, "name")
#=> [{"name"=>"Anita 1", "time"=>18, "wt"=>2.31, "inbr"=>"79","fnbr"=>"-2.31"}
# {"name"=>"Anita 22", "time"=>13, "wt"=>-12.4, "inbr"=>"654","fnbr"=>"12.4"},
# {"name"=>"bubba", "time"=>23, "wt"=>12.84, "inbr"=>"-654","fnbr"=>"-1284e-2"}]
sort_by_value(arr, "name", false)
#=> [{"name"=>"bubba", "time"=>23, "wt"=>12.84, "inbr"=>"-654", "fnbr"=>"-1284e-2"},
# {"name"=>"Anita 22", "time"=>13, "wt"=>-12.4, "inbr"=>"654", "fnbr"=>"12.4"},
# {"name"=>"Anita 1", "time"=>18, "wt"=>2.31, "inbr"=>"79", "fnbr"=>"-2.31"}]
sort_by_value(arr, "time")
#=> [{"name"=>"Anita 22", "time"=>13, "wt"=>-12.4, "inbr"=>"654", "fnbr"=>"12.4"},
# {"name"=>"Anita 1", "time"=>18, "wt"=>2.31, "inbr"=>"79", "fnbr"=>"-2.31"},
# {"name"=>"bubba", "time"=>23, "wt"=>12.84, "inbr"=>"-654", "fnbr"=>"-1284e-2"}]
sort_by_value(arr, "time", false)
#=> [{"name"=>"bubba", "time"=>23, "wt"=>12.84, "inbr"=>"-654", "fnbr"=>"-1284e-2"},
# {"name"=>"Anita 1", "time"=>18, "wt"=>2.31, "inbr"=>"79", "fnbr"=>"-2.31"},
# {"name"=>"Anita 22", "time"=>13, "wt"=>-12.4, "inbr"=>"654", "fnbr"=>"12.4"}]
sort_by_value(arr, "wt")
#=> [{"name"=>"Anita 22", "time"=>13, "wt"=>-12.4, "inbr"=>"654", "fnbr"=>"12.4"},
# {"name"=>"Anita 1", "time"=>18, "wt"=>2.31, "inbr"=>"79", "fnbr"=>"-2.31"},
# {"name"=>"bubba", "time"=>23, "wt"=>12.84, "inbr"=>"-654", "fnbr"=>"-1284e-2"}]
sort_by_value(arr, "wt", false)
#=> [{"name"=>"bubba", "time"=>23, "wt"=>12.84, "inbr"=>"-654", "fnbr"=>"-1284e-2"},
# {"name"=>"Anita 1", "time"=>18, "wt"=>2.31, "inbr"=>"79", "fnbr"=>"-2.31"},
# {"name"=>"Anita 22", "time"=>13, "wt"=>-12.4, "inbr"=>"654", "fnbr"=>"12.4"}]
sort_by_value(arr, "inbr")
#=> [{"name"=>"bubba", "time"=>23, "wt"=>12.84, "inbr"=>"-654", "fnbr"=>"-1284e-2"},
# {"name"=>"Anita 1", "time"=>18, "wt"=>2.31, "inbr"=>"79", "fnbr"=>"-2.31"},
# {"name"=>"Anita 22", "time"=>13, "wt"=>-12.4, "inbr"=>"654", "fnbr"=>"12.4"}]
sort_by_value(arr, "inbr", false)
#=> [{"name"=>"Anita 22", "time"=>13, "wt"=>-12.4, "inbr"=>"654", "fnbr"=>"12.4"},
# {"name"=>"Anita 1", "time"=>18, "wt"=>2.31, "inbr"=>"79", "fnbr"=>"-2.31"},
# {"name"=>"bubba", "time"=>23, "wt"=>12.84, "inbr"=>"-654", "fnbr"=>"-1284e-2"}]
sort_by_value(arr, "fnbr")
#=> [{"name"=>"bubba", "time"=>23, "wt"=>12.84, "inbr"=>"-654", "fnbr"=>"-1284e-2"},
# {"name"=>"Anita 1", "time"=>18, "wt"=>2.31, "inbr"=>"79", "fnbr"=>"-2.31"},
# {"name"=>"Anita 22", "time"=>13, "wt"=>-12.4, "inbr"=>"654", "fnbr"=>"12.4"}]
sort_by_value(arr, "fnbr", false)
#=> [{"name"=>"Anita 22", "time"=>13, "wt"=>-12.4, "inbr"=>"654", "fnbr"=>"12.4"},
# {"name"=>"Anita 1", "time"=>18, "wt"=>2.31, "inbr"=>"79", "fnbr"=>"-2.31"},
# {"name"=>"bubba", "time"=>23, "wt"=>12.84, "inbr"=>"-654", "fnbr"=>"-1284e-2"}]
回答2:
If you want to patch the array class you can do as follow, for example:
module ArrayPatch
def sort_by_key(key, order = :asc)
sorted = sort_by { |hash| hash[key] }
sorted.reverse! if order == :desc
sorted
end
end
Array.include ArrayPatch
The default order is :asc
and the order parameter can be skipped:
array.sort_by_key(:name)
# => [{:sid=>"120", :snumber=>"6546546", :name=>"Anita", :time=>2300, :count=>10, :hour=>"2", :avg=>2}, {:sid=>"101", :snumber=>"798798", :name=>"Anita 1", :time=>1800, :count=>32, :hour=>"", :avg=>1}, {:sid=>"160", :snumber=>"6546546", :name=>"Anita 22", :time=>1300, :count=>30, :hour=>"1", :avg=>1}]
Or add :desc to reverse the order:
array.sort_by_key(:name, :desc)
#=> [{:sid=>"160", :snumber=>"6546546", :name=>"Anita 22", :time=>1300, :count=>30, :hour=>"1", :avg=>1}, {:sid=>"101", :snumber=>"798798", :name=>"Anita 1", :time=>1800, :count=>32, :hour=>"", :avg=>1}, {:sid=>"120", :snumber=>"6546546", :name=>"Anita", :time=>2300, :count=>10, :hour=>"2", :avg=>2}]
I omitted any error handling on parameters.
EDIT: This may fail with string representing number: "1", "12", "2"
is an ordered sequence of String class objects, but expectation is "1", "2", "12"
. In case, the best option is https://stackoverflow.com/a/51215213/5239030
回答3:
Input
ary = [{
"sid" => "101",
"snumber" => "798798",
"name" => "Ben",
"time" => 1800,
"count" => 32,
"hour" => "",
"avg" => 1
},
{
"sid" => "160",
"snumber" => "6546546",
"name" => "anita",
"time" => 1300,
"count" => 30,
"hour" => "1",
"avg" => 1
},
{
"sid" => "120",
"snumber" => "6546546",
"name" => "Jake",
"time" => 2300,
"count" => 10,
"hour" => "2",
"avg" => 2
}]
Solution
I assume you want to sort by any column whether its value be a number or a string. I don't know why you used downcase
in your attempt. If that has a special meaning, please mention. I will update the answer.
def sort_by_key(ary, column, order)
# I am working on hashes with stringified keys, so not doing `to_sym` on `column`. You can uncomment below line if your hashes has symbol keys.
# column = column.to_sym
order = order.to_sym
raise 'Unknown sort order' unless %i[asc desc].include?(order)
ary.sort do |x, y|
order == :asc ?
x[column] <=> y[column] :
y[column] <=> x[column]
end
end
sort_by_key(ary, 'sid', :desc)
=> [{"sid"=>"160", "snumber"=>"6546546", "name"=>"anita", "time"=>1300, "count"=>30, "hour"=>"1", "avg"=>1},
{"sid"=>"120", "snumber"=>"6546546", "name"=>"Jake", "time"=>2300, "count"=>10, "hour"=>"2", "avg"=>2},
{"sid"=>"101", "snumber"=>"798798", "name"=>"Ben", "time"=>1800, "count"=>32, "hour"=>"", "avg"=>1}]
sort_by_key(ary, 'name', :asc)
=> [{"sid"=>"101", "snumber"=>"798798", "name"=>"Ben", "time"=>1800, "count"=>32, "hour"=>"", "avg"=>1},
{"sid"=>"120", "snumber"=>"6546546", "name"=>"Jake", "time"=>2300, "count"=>10, "hour"=>"2", "avg"=>2},
{"sid"=>"160", "snumber"=>"6546546", "name"=>"anita", "time"=>1300, "count"=>30, "hour"=>"1", "avg"=>1}]
See how "anita" comes last as all uppercase letter come before downcase letters by default in sorting.
If you want to sort the names irrespective of the case, modify the method as below:
def sort_by_key(ary, column, order)
...
ary.sort do |x, y|
val_x, val_y = [x, y].map { |hash| hash[column] }
val_x, val_y = [val_x, val_y].map(&:downcase) if [val_x, val_y].all? { |v| v.respond_to?(:downcase) }
order == :asc ?
val_x <=> val_y :
val_y <=> val_x
end
end
sort_by_key(ary, 'name', :asc)
=> [{"sid"=>"160", "snumber"=>"6546546", "name"=>"anita", "time"=>1300, "count"=>30, "hour"=>"1", "avg"=>1},
{"sid"=>"101", "snumber"=>"798798", "name"=>"Ben", "time"=>1800, "count"=>32, "hour"=>"", "avg"=>1},
{"sid"=>"120", "snumber"=>"6546546", "name"=>"Jake", "time"=>2300, "count"=>10, "hour"=>"2", "avg"=>2}]
Note: You have the value of key "hour"
stored as strings. So, sorting will work on them like any other string e.g. "hour"
value "12" will come before value "2". To fix this, you can convert string values for "hour"
to integers using String#to_i on them.
来源:https://stackoverflow.com/questions/51209886/ruby-sort-the-multidimensional-array-via-their-key