Превратить вложенный хеш в двумерный массив в Ruby
Я хочу написать метод, который может получить вложенный хэш и вернуть вложенный массив двумерных массивов.
hash_to_a({1=>2, 2=>3, {3=>4, 5=>6}=>7}) # [[1, 2], [2, 3], [[[3, 4], [5, 6]], 7]]
hash_to_a({{5=>{1=>3, 2=>4}}=>{7=>8}}) # [[[[5, [[1, 3], [2, 4]]]], [[7, 8]]]]
hash_to_a({5=>{1=>3, 2=>4}}) # [[5, [[1, 3], [2, 4]]]]
Пока у меня есть это:
def hash_to_a(a_hash)
result = []
a_hash.each { |k, v|
if k.is_a?(Hash)
result << k.to_a
else
result << k
end
if v.is_a?(Hash)
result << v.to_a
else
result << v
end
}
result
end
Результаты, конечно, не желательны
hash_to_a({1=>2, 2=>3, {3=>4, 5=>6}=>7}) # [1, 2, 2, 3, [[3, 4], [5, 6]], 7]
hash_to_a({{5=>{1=>3, 2=>4}}=>{7=>8}}) # [[[5, {1=>3, 2=>4}]], [[7, 8]]]
hash_to_a({5=>{1=>3, 2=>4}}) # [5, [[1, 3], [2, 4]]]
Как я могу достичь желаемых результатов? Я также пытался отречься, но просто не могу обдумать эту проблему.
4 ответа
Начните с самого простого случая, без вложенности.
def hash_to_a(hash) # { 3 => 4 }
hash.map do |k, v| # 3, 4
[k, v] # [3, 4]
end
end
Теперь проблема в том, что для более сложных случаев мы не хотим возвращать ключ или значение, если это хеш, мы сначала хотим преобразовать его из хеша.
def hash_to_a(hash)
hash.map do |k, v|
[hash_to_a(k), hash_to_a(v)]
end
end
Это взорвется в нашем простом случае, потому что 3 не имеет метода #map
, Это потому, что мы пока не рассматриваем базовый вариант. Просто берет строку кода, чтобы не пытаться отображать вещи, которые не являются хэшами. Мы хотим, чтобы он вел себя так же, как наша первая попытка: ничего не делайте, только верните ключ или значение.
def hash_to_a(object)
return object unless object.is_a? Hash
object.map do |k, v|
[hash_to_a(k), hash_to_a(v)]
end
end
И мы сделали.
hash_to_a({1=>2, 2=>3, {3=>4, 5=>6}=>7}) # [[1, 2], [2, 3], [[[3, 4], [5, 6]], 7]]
hash_to_a({{5=>{1=>3, 2=>4}}=>{7=>8}}) # [[[[5, [[1, 3], [2, 4]]]], [[7, 8]]]]
hash_to_a({5=>{1=>3, 2=>4}}) # [[5, [[1, 3], [2, 4]]]]
Вы можете использовать рекурсию
def h_to_a(h)
h.map { |k,v| [k.is_a?(Hash) ? h_to_a(k) : k, v.is_a?(Hash) ? h_to_a(v) : v] }
end
h_to_a({ 1=>2, 2=>3, { 3=>4, 5=>6 }=>7 })
#=> [[1, 2], [2, 3], [[[3, 4], [5, 6]], 7]]
h_to_a({ { 5=>{ 1=>3, 2=>4 } }=>{ 7=>8 } })
#=> [[[[5, [[1, 3], [2, 4]]]], [[7, 8]]]]
h_to_a({ 5=>{ 1=>3, 2=>4 } })
#=> [[5, [[1, 3], [2, 4]]]]
Это очевидно работает с любым уровнем вложенности.
Что насчет этого:
def deep_to_array(hash)
return hash unless hash.is_a?(Hash)
array = hash.to_a
array.each_with_index do |(k,v), index|
array[index][0] = deep_to_array(k)
array[index][1] = deep_to_array(v)
end
array
end
Или краткий:
def deep_to_array2(hash)
return hash unless hash.is_a?(Hash)
hash.map do |k,v|
[deep_to_array2(k), deep_to_array2(v)]
end
end
Пример:
deep_to_array(({1=>2, 2=>3, {3=>4, 5=>6}=>7}))
=> [[1, 2], [2, 3], [[[3, 4], [5, 6]], 7]]
Еще один вариант (с использованием встроенного Hash#to_a
)
Как метод класса:
class Hash
def flatten_to_a
to_a.map {|i| i.is_a?(Array) ? i.map {|i| i.is_a?(Hash) ? i.flatten_to_a : i} : i}
end
end
Как автономный метод:
def f2a hash
return hash unless Hash === hash
hash.to_a.map {|i| i.is_a?(Array) ? i.map {|i| i.is_a?(Hash) ? i.flatten_to_a : i} : i}
end