【发布时间】:2013-11-27 06:14:47
【问题描述】:
让我们有这个哈希:
hash = {"a" => 1, "b" => {"c" => 3}}
hash.get_all_keys
=> ["a", "b", "c"]
我如何获取所有密钥,因为 hash.keys 只返回 ["a", "b"]
【问题讨论】:
-
谢谢,但以后在选择答案之前请考虑稍等片刻,因为这可能会阻止其他可能提供更好的答案。
让我们有这个哈希:
hash = {"a" => 1, "b" => {"c" => 3}}
hash.get_all_keys
=> ["a", "b", "c"]
我如何获取所有密钥,因为 hash.keys 只返回 ["a", "b"]
【问题讨论】:
这将为您提供任何嵌套级别的所有键的数组。
def get_em(h)
h.each_with_object([]) do |(k,v),keys|
keys << k
keys.concat(get_em(v)) if v.is_a? Hash
end
end
hash = {"a" => 1, "b" => {"c" => {"d" => 3}}}
get_em(hash) # => ["a", "b", "c", "d"]
【讨论】:
我发现grep 在这里很有用:
def get_keys(hash)
( hash.keys + hash.values.grep(Hash){|sub_hash| get_keys(sub_hash) } ).flatten
end
p get_keys my_nested_hash #=> ["a", "b", "c"]
我喜欢这个解决方案,因为它很短,但读起来非常好。
【讨论】:
keys_only.rb
# one-liner
def keys_only(h); h.map { |k, v| v = v.first if v.is_a?(Array); v.is_a?(Hash) ? [k, keys_only(v)] : k }; end
# nicer
def keys_only(h)
h.map do |k, v|
v = v.first if v.is_a?(Array);
if v.is_a?(Hash)
[k, keys_only(v)]
else
k
end
end
end
hash = { a: 1, b: { c: { d: 3 } }, e: [{ f: 3 }, { f: 5 }] }
keys_only(hash)
# => [:a, [:b, [[:c, [:d]]]], [:e, [:f]]]
P.S.:是的,它看起来像一个词法分析器:D
# one-liner
def print_keys(a, n = 0); a.each { |el| el.is_a?(Array) ? el[1] && el[1].class == Array ? print_keys(el, n) : print_keys(el, n + 1) : (puts " " * n + "- #{el}") }; nil; end
# nicer
def print_keys(a, n = 0)
a.each do |el|
if el.is_a?(Array)
if el[1] && el[1].class == Array
print_keys(el, n)
else
print_keys(el, n + 1)
end
else
puts " " * n + "- #{el}"
end
end
nil
end
> print_keys(keys_only(hash))
- a
- b
- c
- d
- e
- f
【讨论】:
def get_all_keys(hash)
hash.map do |k, v|
Hash === v ? [k, get_all_keys(v)] : [k]
end.flatten
end
【讨论】:
请看下面的代码:
hash = {"a" => 1, "b" => {"c" => 3}}
keys = hash.keys + hash.select{|_,value|value.is_a?(Hash)}
.map{|_,value| value.keys}.flatten
p keys
结果:
["a", "b", "c"]
新的解决方案,考虑到@Bala 的 cmets。
class Hash
def recursive_keys
if any?{|_,value| value.is_a?(Hash)}
keys + select{|_,value|value.is_a?(Hash)}
.map{|_,value| value.recursive_keys}.flatten
else
keys
end
end
end
hash = {"a" => 1, "b" => {"c" => {"d" => 3}}, "e" => {"f" => 3}}
p hash.recursive_keys
结果:
["a", "b", "e", "c", "d", "f"]
【讨论】:
hash = {"a" => 1, "b" => {"c" => {"c" => 3}}, "c" => {"b" => 3}},这可能会失败
还可以处理包含哈希的嵌套数组
def all_keys(items)
case items
when Hash then items.keys + items.values.flat_map { |v| all_keys(v) }
when Array then items.flat_map { |i| all_keys(i) }
else []
end
end
【讨论】:
class Hash
def get_all_keys
[].tap do |result|
result << keys
values.select { |v| v.respond_to?(:get_all_keys) }.each do |value|
result << value.get_all_keys
end
end.flatten
end
end
hash = {"a" => 1, "b" => {"c" => 3}}
puts hash.get_all_keys.inspect # => ["a", "b", "c"]
【讨论】:
tap 的有趣用法。您可能想使用__method__ 代替:get_all_keys。如果方法被重命名并且没有改变,则可能需要一段时间才能找到错误。旁白:你知道为什么respond to? 不检查它的参数是否是现有方法吗?
__method__ 的想法。我不明白你对respond_to?行为的问题,请解释一下。好像我错过了一些重要的事情。
v.respond_to?(sym) => 对于不是方法的任何符号 sym 为 false。 Ruby 可以在运行时检查 sym 是否是一个方法,如果不是则引发异常,但它显然没有这样做。我想知道为什么。我突然想到,要使 method_missing 有用,Ruby 可能永远不会检查 Symbol 是否是现有方法。
methods 数组中,因此无法识别 method_missing 是否可能响应。如果不实际运行该方法,几乎不可能分析method_missing 的所有可能行为,这可能会产生副作用。有趣的阅读 Ruby 1.9.2 中的新 respond_to_missing?:blog.marc-andre.ca/2010/11/15/methodmissing-politely
这是另一种方法:
def get_all_keys(h)
h.each_with_object([]){|(k,v),a| v.is_a?(Hash) ? a.push(k,*get_all_keys(v)) : a << k }
end
hash = {"a" => 1, "b" => {"c" => {"d" => 3}}}
p get_all_keys(hash)
# >> ["a", "b", "c", "d"]
【讨论】:
each_with_object 被大大低估了。例如,在这里,人们经常会看到reduce([])(又名inject),但这需要将保存键的数组返回到块中,这可能需要最后那个丑陋的; keys)。我在这里考虑了each_with_object,但决定在之前使用keys = [],在之后使用keys。人们经常面临这样的选择。我认为更简单的方法更具可读性,但它确实增加了两行。您对此有何看法?
each_with_object。对我来说,它在 Ruby 中看起来更具表现力。正如我经常发现的 Ruby 专家喜欢简短而富有表现力的代码。我厌倦了做同样的事情。 :)
我确信有一个更优雅的解决方案,但这个选项有效:
blah = {"a" => 1, "b" => {"c" => 3}}
results = []
blah.each do |k,v|
if v.is_a? Hash
results << k
v.each_key {|key| results << key}
else
results << k
end
end
puts results
【讨论】:
hash.keys 是我见过的最简单的返回哈希键值数组的方法。
【讨论】:
{ a: { b: 1 } }.keys # [:a] :(.