Ruby sort the multidimensional array via their key

南楼画角 提交于 2019-12-13 00:23:09

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!