Сортировка массива по атрибуту, который может быть равен нулю в некоторых элементах - PullRequest
18 голосов
/ 18 декабря 2010

У меня есть массив объектов

[<#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 выдает ошибку, потому что времяэто ноль.

Я сейчас использую уродливый способ сортировки, но я уверен, что есть хороший способ сделать это

starred=[]
@answers.each {|a| (starred << a) if a.starred }
@answers=@answers-starred
starred=starred.sort_by {|a| a.starred }.reverse
@answers=starred+@answers

Ответы [ 3 ]

32 голосов
/ 18 декабря 2010
starred.sort_by { |a| [a ? 1 : 0, a] }

Когда нужно сравнить два элемента, он сравнивает массивы. Когда Ruby сравнивает массивы (вызывает метод ===), он сравнивает 1-й элемент и переходит ко 2-м элементам, только если 1-й равен. ? 1 : 0 гарантирует, что Fixnum будет первым элементом, поэтому не должно быть ошибок.

Если вы сделаете ? 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]
7 голосов
/ 27 марта 2013

Если вы хотите, чтобы нули появлялись первыми (эквивалентными нулю):

@answers.sort_by { |a| a.star or 0 }

Если вы хотите, чтобы они появлялись последними, вы можете заменить ноль на Max Int., но это кажется слишком хакерским.Это могло бы быть лучше:

@answers.select(&:starred?).sort_by(&:star) + @answers.reject(&:starred?)

Ответ, предоставленный Накилоном, блестящий, хотя вы, по сути, сортируете дважды по двум различным атрибутам.Для большинства ситуаций этого вполне достаточно.

3 голосов
/ 05 октября 2017

Просто используйте value.to_i

starred.sort_by { |a| a.to_i }.reverse
...