Is it possible to sort a list of objects depending on the individual object's response to a method?

时间秒杀一切 提交于 2019-12-05 00:44:20
AlexChaffee

Sure. Ideally we'd do something like this using sort_by!:

@products.sort_by! {|product| product.on_sale?}

or the snazzier

@products.sort_by!(&:on_sale?)

but sadly, <=> doesn't work for booleans (see Why doesn't sort or the spaceship (flying saucer) operator (<=>) work on booleans in Ruby?) and sort_by doesn't work for boolean values, so we need to use this trick (thanks rohit89!)

@products.sort_by! {|product| product.on_sale? ? 0 : 1}

If you want to get fancier, the sort method takes a block, and inside that block you can use whatever logic you like, including type conversion and multiple keys. Try something like this:

@products.sort! do |a,b|
  a_value = a.on_sale? ? 0 : 1
  b_value = b.on_sale? ? 0 : 1
  a_value <=> b_value
end

or this:

@products.sort! do |a,b|
  b.on_sale?.to_s <=> a.on_sale?.to_s
end

(putting b before a because you want "true" values to come before "false")

or if you have a secondary sort:

@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

Note that sort returns a new collection, which is usually a cleaner, less error-prone solution, but sort! modifies the contents of the original collection, which you said was a requirement.

@products.sort_by {|product| product.on_sale? ? 0 : 1}

This is what I did when I had to sort based on booleans.

No need to sort:

products_grouped = @products.partition(&:on_sale?).flatten(1)

Ascending and descending can be done by inter changing "false" and "true"

Products.sort_by {|product| product.on_sale? ==  true ? "false" : "true" }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!