开发者

How do you access nested elements of a hash with a single string key?

I'm fooling around with Ruby and basically I have

@trans = { :links => {
    :quick_notes => "aaaaaaa"
  }
}

I want to call something like

d开发者_如何学运维ef t
  #...something
end
t('links.quick_notes')

to access

trans[:links][:quick_notes]

I'm basically trying to achieve the same functionality like when using Internationalizations

I18n.t('something.other.foo') 

sofar I came up with this approach

 def t(key)
   a=''
   key.to_s.split('.').each{|key|  a+="[:#{key}]" } 
   #now a == "[:links][:quick_notes]"
   #but I cant figure out how can I call it on  @trans variable

 end

 t('links.quick_notes')

Any ideas ? thanx


You can get there with inject:

def t(key)
    key.to_s.split('.').inject(@trans) { |h, k| h[k.to_sym] }
end

Error checking and "no such entry" checking is left as an exercise.


For what it's worth, here is some manky code that shows the use of recursion.

def getKeyChain (obj, parts)
  if !obj || parts.size == 0
    # this is the base case
    obj
  else
    # this is the recursive case
    key = parts[0]
    if key.match(/^:/)
      key = key[1..key.size].to_sym
    end
    # each time recursing, pass a new state.
    # in this case, the value for the "key" and the remaining parts
    # that need resolving
    getKeyChain(obj[key], parts[1..parts.size])
  end
end

def getCompoundKey (obj, compound)
  # helper makes it easier to call while not making the
  # recursive function more complex.
  getKeyChain(obj, compound.split("."))
end

h0 = {:x => "hello"}
h1 = {"a" => {:b => "world"}}
puts getCompoundKey(h0, ":x")   # => hello
puts getCompoundKey(h1, "a.:b") # => world

Many improvements can be made... "use at own peril".

Happy coding.


If you are looking for something that interpolates %{variable}, including Array support, I've been using this:

def interpolate(string, hash)
  string.gsub(/%\{([^\}]*)\}/).each { |match| match[/^%{(.*)}$/, 1].split('.').inject(hash) { |h, k| h[(k.to_s == k.to_i.to_s) ? k.to_i : k.to_sym] } }
end


Since Ruby 2.3 Hash has a dig method. "no such entry" checking is built in: if any of the keys is missing, then the whole expression returns nil. Usage:

@trans = { :links => {
    :quick_notes => "aaaaaaa"
  }
}

def t (str)
  syms = str.split(".").map(&:to_sym)
  @trans.dig(*syms)
end
p t('links.quick_notes')  # => "aaaaaaa"


Another addition - If you have json or a hash that includes arrays as well as hashes - maybe something like this:

@trans = { :links => [ {
    :quick_notes => "aaaaaaa"
  },
  {
    :quick_notes => "bbbbbbb"
  } ],
  :page => 1
}

You can include a test of position in an array, you can do this sort of thing.

def t(key)
  key.split('.').inject(@trans) { |h,v| h.send('[]',v.to_i.to_s.eql?(v) ? v.to_i : v) }
end

This would allow you to pass a key like this

t('links.1.quick_notes')

and get the answer 'bbbbbb'

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜