我是红宝石的新手,我很难理解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]
仅返回一个数字。这种方法在后台有什么作用?该文档对初学者不太友好。
答案 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"}]