Можно ли отсортировать список объектов в зависимости от ответа отдельного объекта на метод?
Я хочу показать галерею products
, где я включаю продукты, которые продаются и не продаются. Только я хочу, чтобы products
, которые продаются, появятся в начале списка, а объекты, которые не продаются, появятся в конце списка.
Простым способом для этого является создание двух списков, затем их объединение (один список объектов on_sale? и один список объектов not_sale?):
available_products = []
sold_products = []
@products.each do |product|
if product.on_sale?
available_products << product
else
sold_products << product
end
end
., Но для структуры моего существующего приложения это потребует чрезмерного количества рефакторинга из-за странности моего кода (я теряю свою разбивку на страницы, и я бы предпочел не рефакторинг). Было бы проще, если бы был способ отсортировать существующий список объектов с помощью моего метода product
model on_sale?
, который возвращает логическое значение.
Возможно ли более элегантно перебирать существующий список и сортировать его по этому истинному или ложному значению в рельсах? Я только спрашиваю, потому что так много я не знаю о скрытых в этом framework/language (ruby), и я хотел бы знать, были ли они работают, было сделано передо мной.
Ответы
Ответ 1
Конечно. В идеале мы сделали бы что-то подобное, используя sort_by!
:
@products.sort_by! {|product| product.on_sale?}
или мерцающий
@products.sort_by!(&:on_sale?)
но, к сожалению, <=>
не работает для булевых (см. Почему сортировка или оператор космического корабля (летающего тарелки) (< = > ) не работают на булевых в Ruby?) и sort_by
не работает для булевых значений, поэтому нам нужно использовать этот трюк (спасибо rohit89!)
@products.sort_by! {|product| product.on_sale? ? 0 : 1}
Если вы хотите получить fancier, метод sort
принимает блок, и внутри этого блока вы можете использовать любую логику, которая вам нравится, включая преобразование типов и несколько ключей. Попробуйте что-то вроде этого:
@products.sort! do |a,b|
a_value = a.on_sale? ? 0 : 1
b_value = b.on_sale? ? 0 : 1
a_value <=> b_value
end
или это:
@products.sort! do |a,b|
b.on_sale?.to_s <=> a.on_sale?.to_s
end
(помещая b перед a, потому что вы хотите, чтобы значения "true"
приходили раньше "false"
)
или если у вас есть вторичная сортировка:
@products.sort! do |a,b|
if a.on_sale? != b.on_sale?
b.on_sale?.to_s <=> a.on_sale?.to_s
else
a.name <=> b.name
end
end
Обратите внимание, что sort
возвращает новую коллекцию, которая обычно является более чистым, менее подверженным ошибкам решением, но sort!
изменяет содержимое оригинальной коллекции, о которой вы говорили, было требованием.
Ответ 2
@products.sort_by {|product| product.on_sale? ? 0 : 1}
Это то, что я сделал, когда мне приходилось сортировать на основе булевых элементов.
Ответ 3
Не нужно сортировать:
products_grouped = @products.partition(&:on_sale?).flatten(1)
Ответ 4
Восходящий и нисходящий могут быть сделаны путем изменения "ложных" и "истинных"
Products.sort_by {|product| product.on_sale? == true ? "false" : "true" }