按一组参数对 Ruby 数组进行排序,回退到下一个参数,以防它未定义

Sort Ruby array by a set of parameters, fall back to next parameter in case it's undefined

在 Ruby 中,根据可能存在或不存在的 order 属性对对象数组进行排序的最佳方法是什么?如果不存在,则回退到基于名为 title?

的属性

你可以试试

def sort_array(array)
  sort_by_property_name = sort_by_property_present?(array, :order, :title)
  array.sort_by { |ob| ob.public_send(sort_by_property_name) }
end

def sort_by_property_present?(array, primary_name, fallback_name)
  array.all? { |ob| ob.respond_to?(name) } || return fallback_name
  primary_name
end

如果我明白你是怎么做的:

arr1 = [{order: 1, title: 2},{title: 4},{order: 2, title: 1}]
arr2 = [{order: 1, title: 2},{order: 7, title: 4},{order: 2, title: 1}]

def sort_it prop1, prop2, ar
  ar.map{|el| el[prop1]}.include?(nil) ?
     ar.sort_by{|el| el[prop2]}
    :
     ar.sort_by{|el| el[prop1]}
end

p sort_it(:order, :title, arr1)
p sort_it(:order, :title, arr2)

给出:

#=> [{:order=>2, :title=>1}, {:order=>1, :title=>2}, {:title=>4}]
#=> [{:order=>1, :title=>2}, {:order=>2, :title=>1}, {:order=>7, :title=>4}]

所以,算法很简单:select 所有对象的属性(在我们的例子中是 :order),如果输出临时数组至少包含一个 nil,则按给定的秒排序属性,否则 -- 首先。

不确定这是否是您想要的,但一个快速的解决方案可能是:

arr = [{a:"never", b:"anna"}, {a:"always", b:"bob"}, {b:"colin"}, {b:"abe"}]
arr.sort_by! {|o| o[:a] ? o[:a] : o[:b] }
#=> [{:b=>"abe"}, {:a=>"always", :b=>"bob"}, {:b=>"colin"}, {:a=>"never", :b=>"anna"}]

以下是如何在 Ruby 中使用回退执行排序:

Item = Struct.new(:order, :title)

items = [
  Item.new(nil, "d"),
  Item.new(nil, "b"),
  Item.new(1,   "a"),
  Item.new(3,   "c"),
  Item.new(2,   "e")
]

sorted_items = items.sort do |a, b|
  if a.order && b.order
    a.order <=> b.order
  elsif a.order || b.order
    # This prioritizes all items with an order
    a.order ? -1 : 1
  else
    a.title.to_s <=> b.title.to_s
  end
end

require 'awesome_print'
ap sorted_items

# [
#     [0] {
#         :order => 1,
#         :title => "a"
#     },
#     [1] {
#         :order => 2,
#         :title => "e"
#     },
#     [2] {
#         :order => 3,
#         :title => "c"
#     },
#     [3] {
#         :order => nil,
#         :title => "b"
#     },
#     [4] {
#         :order => nil,
#         :title => "d"
#     }
# ]

我还要说一下,如果您要从数据库中获取记录,那么最好在 SQL 查询中进行排序。如果 Item 是一个 ActiveRecord 模型,你可以这样做:

Item.order('order ASC NULLS LAST, title ASC')

(NULLS LAST can be used in Postgres, check out this answer for MySQL.)

假设您想根据可能存在或不存在的 field/parameter 进行排序,我假设:

  1. 当参数存在时按它排序。
  2. 当不可用时回退到下一个参数。

请查看以下代码,它可以根据多个字段对对象数组进行排序,如果该字段不可用,系统会不断回退到下一个字段。它是在最后一个字段肯定存在的假设下开发的。

class CondtionalSort
  def run(array: a, keys_to_order_by: keys)
    array.sort do |e1, e2|
      keys_to_order_by.each do |key|
        break e1[key] <=> e2[key] if (e1.key?(key) && e2.key?(key))
      end
    end
  end
end


ArrayTest = [{order: 1, title: 2},{title: 4},{order: 2, title: 1}]
ArrayTest_SORTED = [{:order=>1, :title=>2}, {:order=>2, :title=>1}, {:title=>4}]

sorter = CondtionalSort.new
sorter.run array: ArrayTest, keys_to_order_by: [:order, :title]

我只是使用数组作为 sort_by:

# sample data:
Item = Struct.new(:property1, :property2, :property3)
collection = [Item.new("thing1", 3, 6), Item.new("thing1", 3, 1), Item.new("aaa", 1,1) ]

# sort
collection.sort_by{|item| [item.property1, item.property2, item.property3] }
# => [#<struct Item property1="aaa", property2=1, property3=1>, 
      #<struct Item property1="thing1", property2=3, property3=1>, 
      #<struct Item property1="thing1", property2=3, property3=6>]