2017-01-13 23 views
1

它應該很容易,但我找不到合適的解決方案。 爲第一級按鍵:如何在Ruby hash中動態設置嵌套鍵值

resource.public_send("#{key}=", value) 

foo.bar.lolo

我知道,我能得到它像下面這樣:

'foo.bar.lolo'.split('.').inject(resource, :send) 

resource.instance_eval("foo.bar.lolo") 

,但如何將值設置爲最後一個變量假設我不知道該嵌套級,它可能是第二或第三。

有沒有一個通用的方法來做到這一點,所有級別? 我的例子,我可以做如下所示:

resource.public_send("fofo").public_send("bar").public_send("lolo=", value) 
+0

'resource.public_send(「#{key} =」,value)'不會在散列中設置任何內容。 – mudasobwa

+0

不,它不適用於內嵌對象的嵌套鍵。 所以它爲'obj.fofo'工作,但它從來沒有爲'bject.fofo.lolo'工作,它給出了未定義的方法 – user181452

+0

請寫一些事實和信息不僅批評:)這是網站的重點,而不是炫耀你知道,我不知道。告訴我我的錯誤,這比完全無用的評論要好得多。我無法做任何事情。 – user181452

回答

3

答案哈希值,只是出於好奇:

hash = { a: { b: { c: 1 } } } 
def deep_set(hash, value, *keys) 
    keys[0...-1].inject(hash) do |acc, h| 
    acc.public_send(:[], h) 
    end.public_send(:[]=, keys.last, value) 
end 

deep_set(hash, 42, :a, :b, :c) 
#⇒ 42 
hash 
#⇒ { a: { b: { c: 42 } } } 
+0

這不是和hash#bury基本相同嗎?即[this](https://github.com/dam13n/ruby-bury) –

+0

@maxple我不知道'埋葬'是什麼。這是純粹的紅寶石。 – mudasobwa

+0

這就像是輔助挖掘。就像我連接的寶石一樣。只是說人們通常稱這種功能爲埋藏。 –

0

散列紅寶石中默認情況下不給你這些點的方法。

你可以連送話費(這個工程的任何對象,但你不能訪問哈希鍵這樣正常):

"foo".send(:downcase).send(:upcase) 

當嵌套哈希工作的可變性的棘手概念是相關的。例如:

hash = { a: { b: { c: 1 } } } 
    nested = hash[:a][:b] 
    nested[:b] = 2 
    hash 
    # => { a: { b: { c: 2 } } 

「可變性」在這裏是指當你存儲嵌套散列成一個獨立的變量,它仍然是實際上是一個指針到原來的哈希值。可變性對於這樣的情況很有用,但如果你不理解它,它也會產生錯誤。

您可以將:a:b分配給變量,使其在某種意義上成爲「動態」。

有更先進的方法來做到這一點,如在新的Ruby

versions. 

    hash = { a: { b: { c: 1 } } } 
    keys_to_get_nested_hash = [:a, :b] 
    nested_hash = hash.dig *keys_to_get_nested_hash 
    nested_hash[:c] = 2 
    hash 
    # => { a: { b: { c: 2 } } } 

如果使用OpenStruct那麼你可以給你的哈希值點方法存取。說實話,鏈接send調用不是我經常使用的東西。如果它可以幫助你編寫代碼,那很好。但是你不應該發送用戶生成的輸入,因爲它不安全。

+2

'hash.public_send(:[],:a).public_send(:[] =,:b,42)'完美的工作,沒有什麼棘手的。 – mudasobwa

0

雖然你可以實現一些方法做的事情,你讓他們現在設置方式,我強烈建議你重新考慮你的數據結構。

爲了闡明一些術語,您示例中的key不是關鍵,而是方法調用。在Ruby中,當你有像my_thing.my_other_thing這樣的代碼時,my_other_thing總是一種方法,並且永遠不是一個密鑰,至少不是該術語的正確含義。

確實,您可以通過以這種方式鏈接對象來創建一個類似於散列的結構,但這存在真正的代碼異味。如果您認爲foo.bar.lolo是一種在哈希中查找嵌套的lolo密鑰的方法,那麼您應該使用常規哈希。

x = {foo: {bar: 'lolo'}} 
x[:foo][:bar] # => 'lolo' 
x[:foo][:bar] = 'new_value' # => 'new_value' 

此外,雖然send/instance_eval方法可以這種方式使用,但它不是最佳實踐,甚至可能造成安全問題。