Ruby: получить все ключи в хеше (включая вспомогательные ключи)

пусть этот хеш:

hash = {"a" => 1, "b" => {"c" => 3}}
hash.get_all_keys 
=> ["a", "b", "c"]

как я могу получить все ключи, поскольку hash.keys возвращает только ["a", "b"]

Ответ 1

Это даст вам массив всех ключей для любого уровня вложенности.

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"]

Ответ 2

Я нахожу 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"]

Мне нравится решение, поскольку оно короткое, но оно читается очень хорошо.

Ответ 3

def get_all_keys(hash)
  hash.map do |k, v|
    Hash === v ? [k, get_all_keys(v)] : [k]
  end.flatten
end

Ответ 4

Пожалуйста, посмотрите следующий код:

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.

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"]

Ответ 5

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"]

Ответ 6

Вот еще один подход:

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"]

Ответ 7

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

Ответ 8

Я уверен, что есть более элегантное решение, но этот вариант работает:

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

Ответ 9

Версия, которая сохраняет иерархию клавиш

  • Работает с массивами
  • Работает с вложенными хешами

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 }; nil; 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

Ответ 10

Также разберитесь с вложенными массивами, которые содержат хэши

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