Сортировка массива ruby объектов по атрибуту, который может быть nil
У меня есть массив объектов, которые мне нужно отсортировать по атрибуту position, который может быть целым или нулевым, и мне нужны объекты, у которых позиция nil находится в конце массива. Теперь я могу заставить позицию вернуть некоторое значение, а не nil, чтобы array.sort не терпел неудачу, но если я использую 0 в качестве этого значения по умолчанию, тогда он помещает эти объекты в начало сортировки. Какой лучший способ сделать это? должен ли я просто устанавливать значения nil на какое-то смехотворно большое число, которое "почти" всегда гарантировано в конце? или есть какой-то другой способ, я могу заставить метод array.sort помещать объекты атрибута nil в конец массива? код выглядит следующим образом:
class Parent
def sorted_children
children.sort{|a, b| a.position <=> b.position}
end
end
class Child
def position
category ? category.position : #what should the else be??
end
end
теперь, если я сделаю 'else' чем-то вроде 1000000000, то он, скорее всего, поместит их в конец массива, но мне не нравится это решение, поскольку оно произвольно
Ответы
Ответ 1
Как насчет Child
в определении <=>
основываться на category.position
, если существует category
, и сортировка элементов без category
, как всегда, больше, чем с category
?
class Child
# Not strictly necessary, but will define other comparisons based on <=>
include Comparable
def <=> other
return 0 if !category && !other.category
return 1 if !category
return -1 if !other.category
category.position <=> other.category.position
end
end
Затем в Parent
вы можете просто вызвать children.sort
.
Ответ 2
Я бы просто подстроил ваш вид, чтобы положить nil
элементы last. Попробуйте что-то вроде этого.
foo = [nil, -3, 100, 4, 6, nil, 4, nil, 23]
foo.sort { |a,b| a && b ? a <=> b : a ? -1 : 1 }
=> [-3, 4, 4, 6, 23, 100, nil, nil, nil]
Это говорит о том, что если a и b являются не-nil, они обычно сортируются, но если один из них равен нулю, верните статус, который больше чем один.
Ответ 3
Я обрабатываю такие вещи, как это:
children.sort_by {|child| [child.position ? 0 : 1,child.position || 0]}
Ответ 4
Справедливости ради, я не очень хорошо знаком с Ruby, так что считайте это скорее идеей алгоритма, а не кодом... и перепишите оператор?: как любой Ruby имеет этот чист.
Не можете ли вы просто проверить нуль в сравнении:
class Parent
def sorted_children
children.sort{|a,b|( a and b ) ? a <=> b : ( a ? -1 : 1 ) }
end
end
Отредактировано для использования кода Glenra, который реализует то же самое, что и мой, но в меньшем (и, вероятно, более легко читаемом) количестве кода.
Ответ 5
Я не делал Ruby через некоторое время, но вы могли бы разделить нулевую проверку на сортировку (и просто разрешить Child # position для возврата null):
def sorted_children
children.reject{|c| c.position.nil?}.sort_by(&:position) +
children.select{|c| c.position.nil?}
end
По общему признанию, это не самое эффективное решение, но оно не имеет никаких магических чисел.
Ответ 6
Вы можете сделать это без переопределения оператора космического корабля путем определения нового метода сравнения.
class Child
include Comparable
def compare_by_category(other)
return 0 if !category && !other.category
return 1 if !category
return -1 if !other.category
category.position <=> other.category.position
end
end
Метод sort
может принимать блок, поэтому вы можете отсортировать его с помощью этого нового метода:
children.sort {|a,b| a.compare_by_category(b) }
Ответ 7
Самое простое решение для меня это
def sorted_children(children)
children.sort_by { |child| child.position || -1}
end