Заменить и получить доступ к значениям во вложенном hash / json путем в Ruby - PullRequest
0 голосов
/ 24 июня 2018

Обращаясь за советом, что, по вашему мнению, было бы лучшим и простым решением для замены и доступа к значениям во вложенном хэше или json переменной path с помощью ruby?

Например, представьте, что у меня есть json или hash с такой структурой:

{  
   "name":"John",
   "address":{  
      "street":"street 1",
      "country":"country1"
   },
   "phone_numbers":[  
      {  
         "type":"mobile",
         "number":"234234"
      },
      {  
         "type":"fixed",
         "number":"2342323423"
      }
   ]
}

И я хотел бы получить доступ или изменить фиксированный номер мобильного телефона по пути, который может быть указан в такой переменной, как эта: "phone_numbers/1/number" (разделитель не имеет значения в этом случае)

Это решение необходимо для извлечения значений из json / hash и иногда замены переменных путем указания пути к нему.Нашел несколько решений, которые могут найти значение по ключу, но это решение не сработает, поскольку есть несколько хешей / json, где имя ключа во многих местах одинаково.

Я видел это: https://github.com/chengguangnan/vine, но он не работает, когда полезная нагрузка похожа на эту, поскольку в данном случае это не своего рода хеш:

[  
   {  
      "value":"test1"
   },
   {  
      "value":"test2"
   }
] 

Надеюсь, у вас есть отличные идеи, как решить эту проблему.

Спасибо!

РЕДАКТИРОВАТЬ: Итак, я попробовал код ниже с этими данными:

    x = JSON.parse('[  
        {  
           "value":"test1"
        },
        {  
           "value":"test2"
        }
     ]')



y = JSON.parse('{  
    "name":"John",
    "address":{  
       "street":"street 1",
       "country":"country1"
    },
    "phone_numbers":[  
       {  
          "type":"mobile",
          "number":"234234"
       },
       {  
          "type":"fixed",
          "number":"2342323423"
       }
    ]
 }')

p x
p y.to_h
p x.get_at_path("0/value") 
p y.get_at_path("name") 

И получил это:

[{"value"=>"test1"}, {"value"=>"test2"}]
{"name"=>"John", "address"=>{"street"=>"street 1", "country"=>"country1"}, "phone_numbers"=>[{"type"=>"mobile", "number"=>"234234"}, {"type"=>"fixed", "number"=>"2342323423"}]}
hash_new.rb:91:in `<main>': undefined method `get_at_path' for [{"value"=>"test1"}, {"value"=>"test2"}]:Array (NoMethodError)

Для y.get_at_path("name") получил nil

1 Ответ

0 голосов
/ 24 июня 2018

Вы можете использовать Hash.dig для получения вложенных значений, он будет продолжать вызывать dig по результату каждого шага, пока не достигнет конца, и Array также имеет dig , поэтому, когда вы достигнете этого массива, все будет работать:

# you said the separator wasn't important, so it can be changed up here
SEPERATOR = '/'.freeze

class Hash
  def get_at_path(path)
    dig(*steps_from(path))
  end

  def replace_at_path(path, new_value)
    *steps, leaf = steps_from path

    # steps is empty in the "name" example, in that case, we are operating on
    # the root (self) hash, not a subhash
    hash = steps.empty? ? self : dig(*steps)
    # note that `hash` here doesn't _have_ to be a Hash, but it needs to
    # respond to `[]=`
    hash[leaf] = new_value
  end

  private
  # the example hash uses symbols as the keys, so we'll convert each step in
  # the path to symbols. If a step doesn't contain a non-digit character,
  # we'll convert it to an integer to be treated as the index into an array
  def steps_from path
    path.split(SEPERATOR).map do |step|
      if step.match?(/\D/)
        step.to_sym
      else
        step.to_i
      end
    end
  end
end

, и тогда его можно будет использовать как таковое (hash содержит ваш пример ввода):

p hash.get_at_path("phone_numbers/1/number") # => "2342323423"
p hash.get_at_path("phone_numbers/0/type")   # => "mobile"
p hash.get_at_path("name")                   # => "John"
p hash.get_at_path("address/street")         # => "street 1"

hash.replace_at_path("phone_numbers/1/number", "123-123-1234")
hash.replace_at_path("phone_numbers/0/type", "cell phone")
hash.replace_at_path("name", "John Doe")
hash.replace_at_path("address/street", "123 Street 1")

p hash.get_at_path("phone_numbers/1/number") # => "123-123-1234"
p hash.get_at_path("phone_numbers/0/type")   # => "cell phone"
p hash.get_at_path("name")                   # => "John Doe"
p hash.get_at_path("address/street")         # => "123 Street 1"

p hash
# => {:name=>"John Doe",
#     :address=>{:street=>"123 Street 1", :country=>"country1"},
#     :phone_numbers=>[{:type=>"cell phone", :number=>"234234"},
#                      {:type=>"fixed", :number=>"123-123-1234"}]}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...