如何访问嵌套散列/数组中的元素,给定类似“a.b.0.x”的路径?
我有一个嵌套的散列/阵列,例如:如何访问嵌套散列/数组中的元素,给定类似“a.b.0.x”的路径?
target = { :a => {
"b" => [
{
:x => "here"
}
]
}
}
其中散列键是从来没有的数字。给定一串由"."
(例如"a.b.0.x"
)表示的target[:a]["b"][0][:x]
之间的索引/键,我如何访问相应的元素?
回答:
path = "a.b.0.x" path.split('.').reduce(target) do |acc, val|
case acc
when Array
break nil unless /\A\d+\z/ === val
acc[val.to_i]
when Hash
next acc[val.to_i] if /\A\d+\z/ === val && acc[val.to_i]
acc[val] || acc[val.to_sym]
else break nil
end
end rescue nil
#⇒ "here"
回答:
鉴于这种访问的边缘情况,我建议创建一个新的类来处理这种情况并将输入均匀化为一致的结构。在这种情况下,Hash
(可能更边缘的情况下评论欢迎)
class DepthAccessor class AccessFailure < StandardError
def initialize(val,current=nil,full=nil,depth=nil)
super(
if full && depth
"Failed to find #{val.inspect} for #{current.inspect}:#{current.class} in #{full.inspect} at depth #{depth}"
else
val
end
)
end
end
def initialize(target)
raise ArgumentError, "#{target.inspect}:#{target.class} must respond_to :each_with_object" unless target.respond_to?(:each_with_object)
@target = homogenize(target)
end
def path_search(path,sep: '.',raise_on_fail: true)
split_path = path.split(sep)
split_path.each_with_index.inject(@target) do |acc,(val,idx)|
begin
acc.is_a?(Hash) ? acc[val] : raise
rescue StandardError
if raise_on_fail
raise AccessFailure.new(val,acc,@target,split_path[0..idx].join(sep))
else
nil
end
end
end
end
private
def homogenize(val)
case val
when Array
val.each_with_index.with_object({}) {|(v,idx),obj| obj[idx.to_s] = homogenize(v) }
when Hash
val.each_with_object({}) { |(k,v),obj| obj[k.to_s] = homogenize(v) }
else
val
end
end
end
当你创建一个新的实例中的所有键都转换为Strings
和所有Array
转换为使用index
为key
Hash
ES
然后使用这样
target = { :a => {
"b" => [
{
:x => "here"
}
]
}
}
DepthAccessor.new(target).path_search('a.b.0.x')
#=> "here"
DepthAccessor.new(target).path_search('a.b.c.x')
#=> Failed to find "c" for {"0"=>{"x"=>"here"}}:Hash in {"a"=>{"b"=>{"0"=>{"x"=>"here"}}}} at depth a.b.c
Full Example
如果Array
小号的预订要求那么这里是另一个例子(有一些额外的功能)Example
以上是 如何访问嵌套散列/数组中的元素,给定类似“a.b.0.x”的路径? 的全部内容, 来源链接: utcz.com/qa/265468.html