[英]Sort array of hashes based on value of key in hash?
我正在嘗試與Vagrant合作,在旋轉Docker容器時執行一些自動化。 Vagrantfiles本質上是Ruby,因此我應該能夠應用Ruby邏輯來協助解決這個問題。
我正在讀取一個conf.d目錄,該目錄中填充了包含配置數據的YAML文件,然后將配置項的哈希值推送到一個數組中。 完成后,我使用.each通過數組,並根據哈希中的一些鍵的值將配置應用於數組中的每個條目。 其中一個鍵是“鏈接”。 link的值將與另一個鍵“name”的值相關聯。
我基本上需要確保帶有link => 'name'
的哈希在hash link => 'name'
name => 'value'
之前的數組中。
輸入和預期輸出的示例:
輸入
containers = [{"name"=>"foo", "ports"=>["80:80", "443:443"], "links"=>["bar", "baz"]}, {"name"=>"bar", "ports"=>["8888:8888"]}, {"name"=>"baz","ports"=>"80:80"}]
預期產出
containers = [{"name"=>"bar", "ports"=>["8888:8888"]}, {"name"=>"baz", "ports"=>"80:80"}, {"name"=>"foo", "ports"=>["80:80", "443:443"], "links"=>["bar", "baz"]}]
最終結果是帶有“鏈接”的任何條目出現在數組中與哈希的名稱鍵匹配的條目之后。 (基本上是基於鏈接密鑰的依賴順序。)
請注意,鏈接容器可能會鏈接到另一個鏈接容器。
這讓我感到困惑,因為我對我需要做的事情有所了解,但卻沒有技術方面來實際弄清楚“如何?” :)
在此先感謝您的任何幫助。
在我看來,最簡單的事情是這樣的:
linkless_configs = []
linked_configs = []
if config_hash.has_key?("links")
linked_configs.push(config_hash)
else
linkless_configs.push(config_hash)
end
然后你可以通過linkless_configs + linked_configs
進行迭代,並保證每個鏈接的配置都在相應的無鏈接配置之后。
或者,如果你必須排序,你可以
containers.sort_by { |config| config.has_key?("links") ? 1 : 0 }
[編輯: @DavidGrayson指出了我的答案存在缺陷。 我會看看能不能找到解決辦法,但如果我不能解決,我擔心可能會出現這種情況,我會刪除答案。 [編輯#2 :哦,我的! 在我最初的編輯后,有人對我的回答進行了評價。 我不確定我現在可以刪除它,但說實話,我已經決定不這樣做,主要是因為我的解釋對任何提出的解決OP問題的方法都有影響。 在余額中有10分,現在更加引人注目。 2#潮]
我相信我理解這個問題。 sort
需要一個總訂單,這是一個部分訂單,其中每對元素a <= b
或a <= b
。 ref后者不是問題,但是部分訂單要求是。 部分順序必須滿足以下公理:
x ≤ x
), x ≤ y
且y ≤ x
則x = y
)和 x ≤ y
y ≤ z
,則x ≤ z
)。 我的訂購只滿足反身性公理。 大衛給出反例:
containers = [h0, h1, h2]
哪里
h0 = {'name'=>'foo', 'links'=>['bar']},
h1 = {'name'=>'a'},
h2 = {'name'=>'bar'},
containers.sort
#=> [{"name"=>"foo", "links"=>["bar"]},
# {"name"=>"a"}, {"name"=>"bar"}]
我的方法Hash#<=>
建立:
h0 = h1
h0 > h2
h1 = h2
如果sort
要發現h0 = h1 = h2
,那么它將通過傳遞性得出結論h0 = h2
(並且不檢查h0 <=> h2
),這可能導致不正確的結果。
大衛還指出o.follows?(self)
應該引發異常,因為我已將其定義為private
。 由於我還沒有遇到異常,我得出的結論是該聲明尚未執行,但我沒有追溯原因,但這是一個小問題(盡管無疑是一個有用的線索)。
我很感謝大衛找出問題所在。 當然,需要公開不正確的答案,但我覺得我也學到了一些有用的東西。
浪潮]
如果我正確理解了這個問題,並且數據提供了有效的排序,我認為你可以按如下方式進行。
class Hash
def <=>(o)
case
when follows?(o) then 1
when o.follows?(self) then -1
else 0
end
end
private
def follows?(o)
key?("links") && self["links"].include?(o["name"])
end
end
containers = [{"name"=>"foo", "ports"=>["80:80", "443:443"],
"links"=>["bar", "baz"]},
{"name"=>"bar", "ports"=>["8888:8888"]},
{"name"=>"baz","ports"=>"80:80"}]
containers.sort
#=> [{"name"=>"baz", "ports"=>"80:80"},
# {"name"=>"bar", "ports"=>["8888:8888"]},
# {"name"=>"foo", "ports"=>["80:80", "443:443"],
# "links"=>["bar", "baz"]}]
附錄
雖然我在假設數據提供有效排序的前提下,但@ Ajedi32詢問當存在循環引用時會發生什么。 我們來看看:
containers = [{"name"=>"foo", "links"=>["bar"]},
{"name"=>"bar", "links"=>["baz"]},
{"name"=>"baz", "links"=>["foo"]}]
containers.sort
#=> [{ "name"=>"baz", "links"=>["foo"] },
# { "name"=>"bar", "links"=>["baz"] },
# { "name"=>"foo", "links"=>["bar"] }]
containers = [{"name"=>"foo", "links"=>["bar"]},
{"name"=>"bar", "links"=>["foo"]}]
containers.sort
#=> [{ "name"=>"bar", "links"=>["foo"] },
# { "name"=>"foo", "links"=>["bar"] }]
這表明,如果不確定沒有循環引用,則應在排序之前檢查該引用。
這應該適合你:
def order_containers(containers)
unordered = containers.dup
ordered = []
names_from_ordered = {}
name_is_ordered = names_from_ordered.method(:[])
until unordered.empty?
container = unordered.find do |c|
c.fetch('links', []).all? &name_is_ordered
end
raise 'container ordering impossible' if !container
ordered << container
unordered.delete(container)
names_from_ordered[container.fetch('name')] = true
end
ordered
end
containers = [
{ 'name'=>'foo', 'links'=>['bar'] },
{ 'name'=>'a', 'links'=>['goo'] },
{ 'name'=>'bar' },
{ 'name'=>'goo', 'links'=>['foo'] },
]
containers = order_containers(containers)
require 'pp'
pp containers
# => [{"name"=>"bar"},
# {"name"=>"foo", "links"=>["bar"]},
# {"name"=>"goo", "links"=>["foo"]},
# {"name"=>"a", "links"=>["goo"]}]
基本思想是我們使用循環,循環的每次迭代都會從輸入列表中找到一個適合添加到輸出列表的容器。 如果容器所依賴的所有容器都已添加到輸出列表中,則容器適合添加到輸出列表中。 然后從輸入列表中刪除容器並將其添加到輸出列表中。
此循環可以以兩種主要方式終止:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.