2011-12-06 16 views
5

Questa domanda è l'inversa di this question.Conversione di un hash in un hash annidato

Dato un hash che dispone di una serie per ciascun tasto come

{ 
    [:a, :b, :c] => 1, 
    [:a, :b, :d] => 2, 
    [:a, :e] => 3, 
    [:f] => 4, 
} 

qual è il modo migliore per convertirlo in un hash nidificato come

{ 
    :a => { 
     :b => {:c => 1, :d => 2}, 
     :e => 3, 
    }, 
    :f => 4, 
} 
+0

nulla "ricorsivo" a tale proposito, che è normale nidificato. –

+0

@dominikh L'hash particolare potrebbe non essere ricorsivo, ma qualunque sia l'algoritmo per renderlo sarà ricorsivo. Ma penso che quello che dici abbia un senso. L'ho modificato – sawa

+3

Cosa hai provato finora? Cosa specifica in particolare della tua soluzione tentata non funziona? – maerics

risposta

4

Ecco una soluzione iterativa, uno ricorsiva è lasciato come esercizio al lettore:

def convert(h={}) 
    ret = {} 
    h.each do |k,v| 
    node = ret 
    k[0..-2].each {|x| node[x]||={}; node=node[x]} 
    node[k[-1]] = v 
    end 
    ret 
end 

convert(your_hash) # => {:f=>4, :a=>{:b=>{:c=>1, :d=>2}, :e=>3}} 
+0

Grazie. La tua risposta è molto meglio che farlo in modo ricorsivo. È grande. – sawa

+0

Perché l'hash vuoto predefinito? Nota cambiando in 'h.each_with_object ({}) do | (k, v), ret |' puoi lasciare la prima e l'ultima riga (anche se non sono sicuro che 'each_with_object' (v1.9 +) fosse disponibile quando hai risposto alla domanda). Potresti anche usare 'reduce', naturalmente. –

1

C'è già una buona risposta, ma ho lavorato su questo soluzione ricorsiva, ecco che è:

def to_nest(hash) 
    {}.tap do |nest| 
    hash.each_pair do |key, value| 
     nodes = key.dup 
     node = nodes.shift 
     if nodes.empty? 
     nest[node] = value 
     else 
     nest[node] ||= {} 
     nest[node].merge!({nodes => value}) 
     end 
    end 
    nest.each_pair do |key, value| 
     nest[key] = to_nest(value) if value.kind_of?(Hash) 
    end 
    end 
end 
3

funzionale algoritmo ricorsivo:

require 'facets' 

class Hash 
    def nestify 
    map_by { |ks, v| [ks.first, [ks.drop(1), v]] }.mash do |key, pairs| 
     [key, pairs.first[0].empty? ? pairs.first[1] : Hash[pairs].nestify] 
    end 
    end 
end 

p {[:a, :b, :c]=>1, [:a, :b, :d]=>2, [:a, :e]=>3, [:f]=>4}.nestify 
# {:a=>{:b=>{:c=>1, :d=>2}, :e=>3}, :f=>4} 
0

Un altro modo:

def convert(h) 
    h.each_with_object({}) { |(a,n),f| f.update({ a.first=>(a.size==1 ? n : 
    convert({ a[1..-1]=>n })) }) { |_,ov,nv| ov.merge(nv) } } 
end 

Prova ora:

h = { 
    [:a, :b, :c] => 1, 
    [:a, :b, :d] => 2, 
    [:a, :e] => 3, 
    [:f] => 4, 
} 

convert(h) #=> {:a=>{:b=>{:d=>2}, :e=>3}, 
      # :f=>4} 
0

Per un hash mista/struttura nidificata di array che puoi usare th è. (Modificato anche per gli array)

def unflatten(h={}) 
    ret = {} 
    h.each do |k,v| 
    node = ret 
    keys = k.split('.').collect { |x| x.to_i.to_s == x ? x.to_i : x } 
    keys.each_cons(2) do |x, next_d| 
     if(next_d.is_a? Fixnum) 
     node[x] ||= [] 
     node=node[x] 
     else 
     node[x] ||={} 
     node=node[x] 
     end 
    end                                                                   
    node[keys[-1]] = v 
    end 
    ret 
end 

a condizione che si utilizzi il sotto per l'appiattimento. (Dot stringa separata per la chiave al posto di array [diviso su se avete bisogno.])

def flatten_hash(hash) 
    hash.each_with_object({}) do |(k, v), h| 
    if v.is_a? Hash 
     flatten_hash(v).map do |h_k, h_v| 
     h["#{k}.#{h_k}"] = h_v 
     end 
    elsif v.is_a? Array 
     flatten_array(v).map do |h_k,h_v| 
     h["#{k}.#{h_k}"] = h_v 
     end 
    else 
     h[k] = v 
    end 
    end 
end 


def flatten_array(array) 
    array.each_with_object({}).with_index do |(v,h),i| 
    pp v,h,i 
    if v.is_a? Hash 
     flatten_hash(v).map do |h_k, h_v| 
     h["#{i}.#{h_k}"] = h_v 
     end 
    elsif v.is_a? Array 
     flatten_array(v).map do |h_k,h_v| 
     h["#{i}.#{h_k}"] = h_v 
     end 
    end 
    end 
end 
0

Utilizzando DeepEnumerable:

require DeepEnumerable 

h = {[:a, :b, :c]=>1, [:a, :b, :d]=>2, [:a, :e]=>3, [:f]=>4} 

h.inject({}){|hash, kv| hash.deep_set(*kv)} 
Problemi correlati