2013-10-05 16 views
5

Say mam Hash, który wygląda tak w Ruby:Tworzenie nowych mieszań pogrupowane według wartości klucza w Ruby

{ :ie0 => "Hi", :ex0 => "Hey",  :eg0 => "Howdy", 
    :ie1 => "Hello", :ex1 => "Greetings", :eg1 => "Good day"} 

Jaki jest dobry sposób, aby włączyć, że do czegoś takiego:

{ "0" => 
    { 
     "ie" => "Hi", "ex" => "Hey", "eg" => "Howdy" 
    }, 
    "1" => 
    { 
     "ie" => "Hello", "ex" => "Greetings", "eg" => "Good day" 
    } 
} 
+3

Na początek spróbuj: a.group_by {| e | e [0] [/ \ d /]} – Mason

+0

Dobra wskazówka, dzięki. – tvalent2

Odpowiedz

3

Pytałeś o dobrej sposób to zrobić, więc odpowiedź brzmi: sposób, że można lub współpracownik zrozumieć i utrzymania sześć miesięcy od teraz.

Po pierwsze, potrzebujesz Hash with autovivification, ponieważ tworzysz zagnieżdżoną strukturę skrótu. Jest to bardzo przydatna kodowania wzór, który uprości kod aplikacji:

# Copied & pasted from the question 
old_hash = { :ie0 => "Hi", :ex0 => "Hey", :eg0 => "Howdy", :ie1 => "Hello", :ex1 => "Greetings", :eg1 => "Good day"} 

# Auto-vivify 
new_hash = Hash.new { |h, k| h[k] = { } } 

Następnie można pętli istniejących kluczy w tym prostym stylu, wyrwanie się z części każdego klawisza, i korzystania z nich, aby zapisać wartość w nowej hash:

old_hash.each_pair do |key, value| 
    key =~ /^(..)(.)$/    # Make a regex group for each string to parse 
    new_hash[$2][$1] = value   # The regex groups become the new hash keys 
end 

puts new_hash 

uzyskać ten wynik:

{"0"=>{"ie"=>"Hi", "ex"=>"Hey", "eg"=>"Howdy"}, "1"=>{"ie"=>"Hello", "ex"=>"Greetings", "eg"=>"Good day"}} 
+0

Tak, mam to samo. Nie wiedziałem jednak o auto-ożywieniu! – tvalent2

+0

Działa doskonale dla mnie, Ruby 2.0.0. Będę edytować, aby dodać pełny kod. – Dogweather

+0

Ah, po prostu nie 'puts new_hash'. Wspaniale! Dziękuję! – tvalent2

3

to nie jest ładna, ale to działa:

input = { :ie0 => "Hi", :ex0 => "Hey",  :eg0 => "Howdy", 
      :ie1 => "Hello", :ex1 => "Greetings", :eg1 => "Good day"} 

output = input.inject({}) do |result, item| 
    item[0] =~ /(?<key>[^\d]+)(?<index>\d+)/ 
    key, index = $1, $2 
    value = item[1] 

    result[index] ||= {} 
    result[index].merge! { key => value } 
    result 
end 

puts output 
+0

Musiałem zrobić 'wynik [indeks] .merge! (Klucz => wartość)' ale działa. Wow, dziękuję! – tvalent2

1

Edit: z moimi przeprosinami do upvoter, teraz raczej dislke tę odpowiedź (ale nie tak bardzo, że będę sam to zrób). Pozwolę mu pozostać tutaj ze względu na jego historyczne znaczenie (i oczywiście za 10 punktów). Wśród wielu niedociągnięć, w wynikach dopuszcza się tylko jednocyfrowe klucze. Zgłosiłem inne rozwiązanie, które mi się bardziej podoba.

h = {ie0: "Hi", ex0: "Hey", eg0: "Howdy", ie1: "Hello", ex1: "Greetings", eg1: "Good day"} 

n = h.keys.map {|k| k.to_s[0..-2]}.uniq.size # => 3 

Hash[*h.to_a.each_slice(n).to_a.map {|s| [s.first.first.to_s[-1], Hash[*s.map {|v| [v.first.to_s[0..-2], v.last]}.flatten]]}.flatten] 
+0

Działa wspaniale, dziękuję. – tvalent2

2
require 'awesome_print' 

hsh = { 
      :ie0 => "Hi", 
      :ex0 => "Hey", 
      :eg0 => "Howdy", 
      :ie1 => "Hello", 
      :ex1 => "Greetings", 
      :eg1 => "Good day" 
     } 

new_hsh = hsh.each_with_object(Hash.new { |h, k| h[k] = { } }) do |(k,v),h| 
     h[k[-1]].merge!({k[0..-2] => v}) 
end 
ap new_hsh 

wyjścia (sformatowane z awesome_print)

{ 
    "0" => { 
     "ie" => "Hi", 
     "ex" => "Hey", 
     "eg" => "Howdy" 
    }, 
    "1" => { 
     "ie" => "Hello", 
     "ex" => "Greetings", 
     "eg" => "Good day" 
    } 
} 
+0

Nice, Arup! (komentarz się skończył, ale to nie było wystarczająco długo ..) –

+0

@CarySwoveland Dziękuję bardzo! :) –

0

Oto podejście używając @ sugestią Masona (w komentarzu na pytanie) użycie group_by.

h = {ie0: "Hi", ex0: "Hey", eg0: "Howdy", ie999: "Hello", ex999: "Greetings", eg999: "Good day"} 

Edit: nowe i ulepszone (?):

p Hash[h.group_by {|e| e[0][/\d+/]}.map {|k,v| [k, Hash[v.map {|a| [a.first.to_s[/[^\d]+/], a.last]}]]}] 

Dawniej miałem:

hh = h.group_by { |e| e[0][/\d+/] } 
hh.each_key {|k| hh[k] = Hash[*[hh[k].map {|v| [v.first.to_s[/[^\d]+/], v.last]}].flatten]} 

sugestie są mile widziane. Znalazłem to interesujące pytanie i cieszyłem się z czytania innych odpowiedzi, które uważam za różnorodne i innowacyjne.

+0

Nie wiedziałem o every_key. Miły. – Dogweather

Powiązane problemy