Сортировка массива на основе атрибута, который может быть равен нулю в некоторых элементах
У меня есть массив объектов
[<#a star=1 val=1>, <#a star=nil val=3> , <#a star=2 val=2>]
Мне нужен массив, который будет сортироваться по времени, а затем val
[ <#a star=2 val=2>, <#a star=1 val=1>, <#a star=nil val=3> ]
но с помощью sort_by выдает ошибку, потому что время равно nil.
Я использую уродливый способ сортировки прямо сейчас, но я уверен, что есть хороший способ сделать это
starred=[]
@answers.each {|a| (starred << a) if a.starred }
@[email protected]
starred=starred.sort_by {|a| a.starred }.reverse
@[email protected]
Ответы
Ответ 1
starred.sort_by { |a| [a ? 1 : 0, a] }
Когда ему приходится сравнивать два элемента, он сравнивает массивы. Когда Ruby сравнивает массивы (вызывает метод ===
), он сравнивает 1-й элемент и переходит во 2-й элемент только в том случае, если первые равны. ? 1 : 0
garantees, у нас будет Fixnum как 1-й элемент, поэтому он не должен быть ошибкой.
Если вы выполните ? 0 : 1
, nil
появится в конце массива вместо начала.
Вот пример:
irb> [2, 5, 1, nil, 7, 3, nil, nil, 4, 6].sort_by { |i| [i ? 1 : 0, i] }
=> [nil, nil, nil, 1, 2, 3, 4, 5, 6, 7]
irb> [2, 5, 1, nil, 7, 3, nil, nil, 4, 6].sort_by { |i| [i ? 0 : 1, i] }
=> [1, 2, 3, 4, 5, 6, 7, nil, nil, nil]
Ответ 2
Если вы хотите, чтобы nils отображались сначала (нуль-эквивалент):
@answers.sort_by { |a| a.star or 0 }
Если вы хотите, чтобы они отображались последними, вы можете заменить ноль на Max Int, но это кажется слишком взломанным. Это может быть лучше:
@answers.select(&:starred?).sort_by(&:star) + @answers.reject(&:starred?)
Ответ, предоставленный Накилоном, блестящий, хотя вы по существу сортируете дважды, по двум различным атрибутам. Для большинства ситуаций это, вероятно, достаточно.
Ответ 3
Просто используйте value.to_i
starred.sort_by { |a| a.to_i }.reverse