2017-01-13 9 views
1

это должно быть легко, но я не смог найти правильное решение. для первых ключей уровня:Как установить динамическое значение вложенного ключа в хэш-код Ruby

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

не в основном такой же, как хэш # похоронить? т. е. [это] (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, чтобы сделать его «динамическим» в некотором смысле.

Есть более продвинутые способы сделать это, такие как в новой Руби

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, то вы можете дать вашим хэши DOT-метод аксессоров. Честно говоря, цепочка звонков 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' 

Кроме того, хотя посыла/instance_eval методы могут быть использованы таким образом, это не лучшая практика, и даже может создавать проблемы безопасности.