Arrays 在Ruby中动态排序哈希数组

Arrays 在Ruby中动态排序哈希数组,arrays,ruby,sorting,Arrays,Ruby,Sorting,我想根据几个动态标准对哈希数组进行排序。假设我有这个数组 persons = [ { id: 1, first_name: "Bill", last_name: "Zamora", age: 37 }, { id: 2, first_name: "Alexia", last_name: "Reyes", age: 70 }, { id: 3, first_name: "Anthony",

我想根据几个动态标准对哈希数组进行排序。假设我有这个数组

persons = [
  {
    id: 1,
    first_name: "Bill",
    last_name: "Zamora",
    age: 37
  },
  {
    id: 2,
    first_name: "Alexia",
    last_name: "Reyes",
    age: 70
  },
  {
    id: 3,
    first_name: "Anthony",
    last_name: "Nelson",
    age: 25
  }
]
我知道您可以使用以下代码轻松地按多个条件对数组进行排序

persons.sort_by!{ |p| [p[:age], p[:first_name]] }
但是,在本例中,数组排序所依据的字段的编号和顺序是硬编码的。在我的例子中,这是在运行时动态确定的。所以我不知道数组要按多少字段排序,也不知道哪些字段按哪个顺序排序

我正在寻找一个优雅的解决方案,使用我以前不知道的配置对象对数组进行排序。这种配置可能如下所示:

sort_settings = [
  {
    field: "first_name",
    order: "asc"
  },
  {
    field: "age",
    order: "desc"
  }
]

我非常感谢在这方面的任何帮助

您需要一种方法,以配置为指导,将给定项转换为排序键:

def build_sort_key_for(item, configuration)
  configuration.map { |entry|
    value = item[entry[:field].to_sym]
    value = -value if entry[:order] == "desc" # this will only work on numeric values
    value
  }
end
那你就把它叫做你的分类吧:

persons.sort_by!{ |p| build_sort_key_for(p, configuration) }

使
“desc”
用于字符串本身是一个挑战,因此留给读者(或一个单独的问题)。

使用
排序依据
按desc顺序对字符串进行排序非常具有挑战性,最好使用“低级”方法,该方法使用
运算符按指定的比较器进行排序。此问题的快速解决方案如下所示:

persons.sort do |a, b|
  comparator = 0

  sort_settings.each do |s|
    a_field = a[s[:field].to_sym]
    b_field = b[s[:field].to_sym]

    comparator = a_field <=> b_field

    comparator = -comparator if s[:order] == "desc"

    break unless comparator == 0
  end

  comparator
end
persons.sort do | a,b|
比较器=0
排序设置。每个do|
a_field=a[s[:field]。to_sym]
b_field=b[s[:field]。to_sym]
比较器=a_字段b_字段
比较器=-比较器如果s[:顺序]=“描述”
除非比较器==0,否则中断
结束
比较器
结束
块必须实现a和b之间的比较,当a跟在b后面时返回-1,当a和b相等时返回0,或者如果b跟在a后面时返回+1


因此,我们迭代
排序设置
并使用
比较指定的字段,它返回
1
0
-1
。如果指定的顺序是
desc
,我们将反转该值。如果comparator返回的值不同于零,则无需继续迭代。

忽略
asc
/
desc
功能,并假设排序键以符号形式给出,且采用以下格式:

sort_settings = [
  :first_name,
  :age,
]
您只需执行以下操作:

persons.sort_by{|p| p.values_at(sort_settings)}
代码

def sort_by_settings(persons, sort_settings)
  sort_mult_by_field = sort_settings.each_with_object({}) do |g,h|
    h[g[:field]] = g[:order] == "asc" ? 1 : -1
  end

  longest_string_by_key = persons.each_with_object(Hash.new(0)) do |g,h|
    g.each { |k,v| h[k] = [h[k], g[k].size].max if sort_mult_by_field.key?(k) &&
      v.is_a?(String) }
  end

  sort_by_arr = persons.each_with_object({}) do |g,h|    
    h[g] = sort_mult_by_field.each_with_object([]) do |(f,m),a|
      gv = g[f]
      a <<
      case gv
      when Integer
        m * gv
      when String
        gv.chars.map { |c| m * c.ord }.concat([m * -256]*(longest_string_by_key[f]-gv.size))
      else # rescue...
      end
    end
  end

  persons.sort_by { |g| sort_by_arr[g] }
end
解释

在第一个示例的计算中,计算了以下中间值

sort_mult_by_field
  #=> {:first_name=>1, :age=>-1}

longest_string_by_key
  #=> {:first_name=>7}

sort_by_arr
  #=> {{:id=>1, :first_name=>"Bill",    :last_name=>"Zamora", :age=>37}=>
  #      [[66, 105, 108, 108, -256, -256, -256], -37],
  #    {:id=>2, :first_name=>"Alexia",  :last_name=>"Reyes",  :age=>70}=>
  #      [[65, 108, 101, 120,  105,   97, -256], -70],
  #    {:id=>3, :first_name=>"Anthony", :last_name=>"Nelson", :age=>25}=>
  #      [[65, 110, 116, 104,  111,  110,  121], -25]}

desc
部分可能没有您想象的那么简单。就为了这个功能,,代码可能非常复杂。最好将
排序设置中的
字段
值作为符号,而不是字符串,因为原始哈希中的键是符号。对于顺序,您可以始终按一个方向排序,然后使用
反向
获得相反的结果。更新:事实上,这只会在按不同顺序的多个字段排序时有所帮助。干得好!!只有使用
“asc”
“desc”
sort_mult_by_field
  #=> {:first_name=>1, :age=>-1}

longest_string_by_key
  #=> {:first_name=>7}

sort_by_arr
  #=> {{:id=>1, :first_name=>"Bill",    :last_name=>"Zamora", :age=>37}=>
  #      [[66, 105, 108, 108, -256, -256, -256], -37],
  #    {:id=>2, :first_name=>"Alexia",  :last_name=>"Reyes",  :age=>70}=>
  #      [[65, 108, 101, 120,  105,   97, -256], -70],
  #    {:id=>3, :first_name=>"Anthony", :last_name=>"Nelson", :age=>25}=>
  #      [[65, 110, 116, 104,  111,  110,  121], -25]}