了解Ruby sort_by

时间:2019-10-18 12:07:48

标签: ruby

我是红宝石的新手,我很难理解sort_by!。这种方法确实做了我不了解的魔术。

这是一个简单的例子:

pc= ["Z6","Z5","Z4"]

c = [
    {
        id: "Z4",
        name: "zlah1"
    },
    {
        id: "Z5",
        name: "blah2"
    },
    {
        id: "Z6",
        name:  "clah3"
    }
]


c.sort_by! do |c|
 pc.index c[:id] 
end

此过程返回:

  

=> [{:id =>“ Z6”,:name =>“ clah3”},{:id =>“ Z5”,:name =>“ blah2”},{:id =>“ Z4” ,:name =>“ zlah1”}]

它以某种方式颠倒了数组的顺序。它是如何做到的? pc.index c[:id]仅返回一个数字。这种方法在后台有什么作用?该文档对初学者不太友好。

2 个答案:

答案 0 :(得分:3)

假设我们得到以下信息:

order = ['Z6', 'Z5', 'Z4']
array = [{id: 'Z4', name: 'zlah1'},
         {id: 'Z5', name: 'blah2'},
         {id: 'Z6', name: 'clah3'},
         {id: 'Z5', name: 'dlah4'}]

请注意,我向问题中给定的数组{id: 'Z5', name: 'dlah4'}添加了第4个 哈希(array)。我这样做是为了使array的两个元素的键:id"Z5")具有相同的值。

现在让我们考虑Ruby如何实现以下目标:

array.sort_by { |hash| order.index(hash[:id]) }
  #=> [{:id=>"Z6", :naCme=>"clah3"},
  #    {:id=>"Z5", :name=>"blah2"},
  #    {:id=>"Z5", :name=>"dlah4"},
  #    {:id=>"Z4", :name=>"zlah1"}] 

这可以通过四个步骤完成。

第1步:创建一个哈希,该哈希将排序标准的值映射到sort_by的接收者的值

sort_map = array.each_with_object(Hash.new { |h,k| h[k] = [] }) do |hash,h|
  h[order.index(hash[:id])] << hash
end
  #=> {2=>[{:id=>"Z4", :name=>"zlah1"}],
  #    1=>[{:id=>"Z5", :name=>"blah2"}, {:id=>"Z5", :name=>"dlah4"}],
  #    0=>[{:id=>"Z6", :name=>"clah3"}]}

h = Hash.new { |h,k| h[k] = [] }使用默认proc 创建一个空散列,该散列在评估时的操作如下:

h[k] << hash

如果h具有密钥k,则该操作将照常执行。但是,如果h没有键k,则会调用proc,从而导致执行操作h[k] = [],然后按常规执行h[k] << hash

此散列中的值必须是数组,而不是array的单个元素,这是因为sort_by的接收者的两个元素可能映射到同一键。请注意,此操作与sort_by的接收者的元素到排序标准的特定映射无关。

第2步:对sort_map的键进行排序

keys = sort_map.keys
  #=> [2, 1, 0] 
sorted_keys = keys.sort
  #=> [0, 1, 2]

步骤3:将sorted_keys映射到sort_map的值

sort_map_values = sorted_keys.map { |k| sort_map[k] }
  #=> [[{:id=>"Z6", :name=>"clah3"}],
  #    [{:id=>"Z5", :name=>"blah2"}, {:id=>"Z5", :name=>"dlah4"}],
  #    [{:id=>"Z4", :name=>"zlah1"}]]

第4步:展平sort_map_values

sort_map_values.flatten
  #=> [{:id=>"Z6", :name=>"clah3"},
  #    {:id=>"Z5", :name=>"blah2"},
  #    {:id=>"Z5", :name=>"dlah4"},
  #    {:id=>"Z4", :name=>"zlah1"}] 

使用sort_by而不是sort(带有块)的优点之一是,对于{{1 }}的接收者,而order.index(hash[:id])将针对其块中的每个成对比较重新计算这些值。如果此操作在计算上昂贵,则可以节省大量时间。

答案 1 :(得分:2)

order = ['Z6', 'Z5', 'Z4']
array = [{id: 'Z4', name: 'zlah1'},
         {id: 'Z5', name: 'blah2'},
         {id: 'Z6', name: 'clah3'}]

array.sort_by { |hash| order.index(hash[:id]) }
#=> [{:id=>"Z6", :name=>"clah3"}, {:id=>"Z5", :name=>"blah2"}, {:id=>"Z4", :name=>"zlah1"}]

这不会神奇地反转数组的顺序。为了解释发生了什么,我们首先需要了解order.index(hash[:id])的作用。使用map方法可以更好地看到这一点。

array.map { |hash| order.index(hash[:id]) }
#=> [2, 1, 0]

就像您看到的那样,具有{em> id 'Z4'的第一个元素将返回数字2,因为'Z4'数组中的order具有索引2。其他所有数组元素也是如此。重新调整后的值用于对对象进行排序,sort_by将始终异步排序,因此上述数组的顺序应变为[0, 1, 2]。但是,实际内容不会被替换,该数字仅用于与其他元素进行比较。因此导致:

#=> [{:id=>"Z6", :name=>"clah3"}, {:id=>"Z5", :name=>"blah2"}, {:id=>"Z4", :name=>"zlah1"}]
相关问题