arr = [
{"name"=>"X", "year"=>"2013-08"},
{"name"=>"X", "year"=>"2000-08"},
{"name"=>"B", "year"=>"2018-05"},
{"name"=>"A", "year"=>"2017-01"},
{"name"=>"C", "year"=>"2016-04"},
{"name"=>"D", "year"=>"2016-04"},
]
Когда части массива должны быть отсортированы иначе, чем другие части массива, я считаю целесообразным разделить массив на связанные части, отсортировать каждую часть отдельно, а затем объединить результаты этих сортировок. Такой подход не только прост для читателей, но и упрощает тестирование и, как правило, не менее эффективен, чем выполнение одного более сложного вида. Здесь мы разделим массив на две части.
x, non_x = arr.partition { |h| h["name"] == 'X' }
#=> [[{"name"=>"X", "year"=>"2013-08"}, {"name"=>"X", "year"=>"2000-08"}],
# [{"name"=>"B", "year"=>"2018-05"}, {"name"=>"A", "year"=>"2017-01"},
# {"name"=>"C", "year"=>"2016-04"}, {"name"=>"D", "year"=>"2016-04"}]]
Сортировать массив x
просто.
sorted_x = x.sort_by { |h| h["year"] }.reverse
#=> [{"name"=>"X", "year"=>"2013-08"}, {"name"=>"X", "year"=>"2000-08"}]
Сортировка non_x
является более сложной, потому что она должна быть отсортирована по уменьшению порядка значений "year"
, а связи должны быть разорваны по значениям "name"
в увеличение порядка. В этой ситуации мы всегда можем использовать Array # sort .
non_x.sort do |g,h|
case g["year"] <=> h["year"]
when -1
1
when 1
-1
when 0
(g["name"] < h["name"]) ? -1 : 1
end
end
#=> [{"name"=>"B", "year"=>"2018-05"}, {"name"=>"A", "year"=>"2017-01"},
# {"name"=>"C", "year"=>"2016-04"}, {"name"=>"D", "year"=>"2016-04"}]
Приложив немного усилий, мы могли бы альтернативно использовать Enumerable # sort_by . Учитывая хэш h
, нам нужно отсортировать либо
[h["year"], f(h["name"])].reverse
, где f
- это метод, который приводит к сортировке h["name"]
в порядке убывания или (примечание .reverse
в дальнейшем)
[f(h["year"]), h["name"]]
, где f
- это метод сортировки h["year"]
в порядке убывания. Последнее легче реализовать из двух. Мы могли бы использовать следующий метод.
def year_str_to_int(year_str)
yr, mon = year_str.split('-').map(&:to_i)
12 * yr + mon
end
Это позволяет нам сортировать non_x
по желанию:
sorted_non_x = non_x.sort_by { |h| [-year_str_to_int(h["year"]), h["name"]] }
#=> [{"name"=>"B", "year"=>"2018-05"}, {"name"=>"A", "year"=>"2017-01"},
# {"name"=>"C", "year"=>"2016-04"}, {"name"=>"D", "year"=>"2016-04"}]
Теперь мы просто объединяем два отсортированных раздела.
sorted_x.concat(sorted_non_x)
#=> [{"name"=>"X", "year"=>"2013-08"}, {"name"=>"X", "year"=>"2000-08"},
# {"name"=>"B", "year"=>"2018-05"}, {"name"=>"A", "year"=>"2017-01"},
# {"name"=>"C", "year"=>"2016-04"}, {"name"=>"D", "year"=>"2016-04"}]