2017-02-23 21 views
0

考慮:如何設置任意長度的「關鍵路徑」的Ruby散列?

h = {foo: {bar: 1}} 

如何設置bar,如果你不知道你有多少個鍵有哪些?

例如:keys = [:foo, :bar]

h[keys[0]][keys[1]] = :ok 

但是,如果密鑰可以是任意長度和H的任意深度的?

+1

你問,給定一個鍵列表(在一個數組中),設置最後一個鍵的值? – Anthony

+0

@Anthony - 是的。這更簡潔。 –

回答

4

2.3及更高版本,那麼你可以使用正是如此:

h.dig(*keys[0..-2])[keys.last] = :ok 

通過哈希並返回遵循路徑它發現了什麼。但將不會複製它發現的內容,因此您可以獲得h中的相同參考。 keys[0..-2]抓取keys的最後一個元素,因此h.dig(*keys[0..-2])會給出h中的{bar: 1}哈希值,那麼您可以通過簡單的賦值對其進行就地修改。

你也可以說:

*head, tail = keys 
h.dig(*head)[tail] = :ok 

如果這不是[0..-2]更清晰的給你。

如果沒有做,那麼你可以做這樣的事情:

*head, tail = keys 
head.inject(h) { |m, k| m[k] }[tail] = :ok 

當然,如果你不能確定由keys指定的路徑存在,那麼你就需要在扔一些nil檢查和決定你應該如何處理keys沒有指定到h的路徑。

+0

我正在嘗試使用dig,但無法弄清楚如何完成這個任務。我真的很喜歡你的方法 – Anthony

+1

不錯。我發現'h.dig(* head)[tail] =:ok'更具可讀性。 –

+0

我想出了'path = keys.dup; last_key = path.pop',但'* head,tail = keys'更好。 –

0

你可以做到這一點遞歸是這樣的:如果你使用Ruby

h = {foo: {bar: 1}} 
keys = [:foo, :bar] 

h2 = { 
    foo: { 
    bar: { 
     baz: { 
     setting: :bad 
     } 
    } 
    } 
} 

keys2 = [:foo, :bar, :baz, :setting] 

def set_nested_value(hash, keys, new_val) 
    if keys.length == 1 
    hash[keys.first] = new_val 
    return 
    end 
    keys.each_with_index do |key, i| 
    if hash[key] 
     set_nested_value(hash[key], keys[i+1..-1], new_val) 
    end 
    end 
end 

set_nested_value(h, keys, :ok) 
p h 
=> {:foo=>{:bar=>:ok}} 

set_nested_value(h2, keys2, :good) 
p h2 

=> {:foo=>{:bar=>{:baz=>{:setting=>:good}}}} 
0

@ muistooshort有最好的答案。

只是爲了好玩,這裏是一個創造性的:

h = { baz: { foo: { bar: 1 } } } 
ObjectSpace.each_object(Hash).find { |h| h.key?(:bar) }[:bar] = :ok 
puts h 
#=> {:baz=>{:foo=>{:bar=>:ok}}} 

免責聲明:不這樣做。 ;)

相關問題