Ruby сортирует многомерный массив по ключу - PullRequest
0 голосов
/ 06 июля 2018

Я хочу отсортировать массив для сортировки в порядке возрастания или убывания. Мой массив содержит ключи хеша и значения, которые я хочу отсортировать, являются float, integer и string (names). Имена могут быть буквенными или буквенно-цифровыми. Я хочу создать метод, который будет обрабатывать все сортировки. Он примет массив, имя столбца и порядок сортировки и вернет остаток отсортированного массива. Ниже приведен вывод JSON. Мой массив содержит хеш-ключи.

[
  {
    "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
  }
]

Я много чего пробовал, но ничего не смог сделать правильно. Вот мой метод:

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

Кто-нибудь может мне помочь?

Ожидаемый результат - отсортированный массив. Скажем, если я хочу отсортировать массив, используя sid в порядке возрастания, тогда результирующий массив будет отсортирован по sid в указанном порядке, аналогично для других ключей. Однако массив может быть отсортирован только по одному ключу за раз.

Ответы [ 3 ]

0 голосов
/ 06 июля 2018

Если вы хотите исправить класс массива, вы можете сделать следующее, например:

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

Порядок по умолчанию :asc, а параметр заказа можно пропустить:

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}]

Или добавьте: desc, чтобы изменить порядок:

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}]

Я пропустил обработку ошибок в параметрах.

РЕДАКТИРОВАТЬ: Может произойти сбой со строкой, представляющей число: "1", "12", "2" является упорядоченной последовательностью объектов класса String, но ожидание составляет "1", "2", "12". На всякий случай лучший вариант - https://stackoverflow.com/a/51215213/5239030

0 голосов
/ 06 июля 2018

Код

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

См. Enumerable # sort_by , Объект # kind_of? и Ядро # Float .

Я предположил, что когда значением является строка, представляющая числовое значение (отличное от комплексного числа), сортировка должна выполняться по соответствующему номеру. Float(v) пытается преобразовать строку v в число с плавающей точкой. В случае успеха возвращается float; иначе это вызывает исключение, которое было спасено (в строке), в результате чего v.downcase возвращается.

Примеры

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"}]
0 голосов
/ 06 июля 2018

Введите

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
      }]

Решение

Я предполагаю, что вы хотите отсортировать по любому столбцу, независимо от того, является ли его значение числом или строкой. Я не знаю, почему вы использовали downcase в своей попытке. Если это имеет особое значение, пожалуйста, укажите. Я обновлю ответ.

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}]

Посмотрите, как слово "anita" стоит последним, когда все заглавные буквы по умолчанию идут перед строчными буквами при сортировке.

Если вы хотите отсортировать имена независимо от регистра, измените метод следующим образом:

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}]

Примечание: Значение ключа "hour" хранится в виде строк. Таким образом, сортировка будет работать на них, как и любая другая строка, например "hour" значение "12" придет до значение "2". Чтобы исправить это, вы можете преобразовать строковые значения для "hour" в целые числа, используя String#to_i для них.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...