сортировка массива объектов ruby ​​по атрибуту, который может быть равен нулю - PullRequest
35 голосов
/ 30 апреля 2009

У меня есть массив объектов, которые мне нужно отсортировать по атрибуту позиции, который может быть целым или нулевым, и мне нужно, чтобы объекты с позицией ноль находились в конце массива. Теперь я могу заставить позицию возвращать какое-то значение, а не nil, чтобы array.sort не потерпела неудачу, но если я по умолчанию использую 0, то эти объекты будут помещены в начало сортировки. Какой лучший способ сделать это? я должен просто установить нулевые значения в какое-то смехотворно большое число, которое «почти» всегда гарантированно будет в конце? или есть какой-то другой способ, которым я мог бы заставить метод 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, то, скорее всего, они будут помещены в конец массива, но мне не нравится это решение, так как оно произвольно

Ответы [ 7 ]

97 голосов
/ 30 апреля 2009

Я бы просто настроил ваш вид, чтобы поставить nil элементов в последнюю очередь. Попробуйте что-то вроде этого.

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, возвращайте статус, который сортирует это значение больше.

16 голосов
/ 30 апреля 2009

Я занимаюсь такими вещами, как это:

 children.sort_by {|child| [child.position ? 0 : 1,child.position || 0]}
15 голосов
/ 30 апреля 2009

Как насчет того, чтобы 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.

6 голосов
/ 30 апреля 2009

Честно говоря, я не очень хорошо знаком с Ruby, поэтому воспринимайте это как идею алгоритма, а не как код ... и переписывайте оператор?: Как бы то ни было в Ruby, который чище.

Не могли бы вы просто проверить на ноль в сравнении:

class Parent
  def sorted_children
     children.sort{|a,b|( a and b ) ? a <=> b : ( a ? -1 : 1 ) }
  end
end

Отредактировано для использования кода Glenra, который реализует то же самое, что и мой, но в меньшем (и, вероятно, легче читаемом) объеме кода.

1 голос
/ 14 мая 2015

Вы можете сделать это без переопределения оператора космического корабля, определив новый метод сравнения.

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) }
1 голос
/ 30 апреля 2009

Я давно не делал Ruby, но вы могли бы отделить проверку нуля от сортировки (и просто позволить позиции Child # вернуть null):

def sorted_children
  children.reject{|c| c.position.nil?}.sort_by(&:position) +
    children.select{|c| c.position.nil?}
end

По общему признанию, это не самое эффективное решение, но у него нет магических чисел.

0 голосов
/ 28 марта 2019

Самое простое решение для меня это

def sorted_children(children)
  children.sort_by { |child| child.position || -1}
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...