2016-04-07 14 views
0

Ich habe einige Textzeilen wie dieseWie Subarray innerhalb jeder Schleife

name carl 
age 34 
name sean 
age 15 

Ich habe diesen Code

lines.each_with_object({}) do |l,r| 
    key, value = l.split(' ', 2) 
    r[key] = value 
end 

die Ausgänge zu machen hinzugefügt, da wird von Sean

überschrieben Was ich suche ist: jede Zeile mit dem name eine neue Elem machen und setzen dort ihre Werte

{ "carl" => { "age" => "34" }, "sean" => { "age" => 15 } } 

Antwort

0
lines.each_slice(2).with_object({}) do |pair, hsh| 
    _key, name, attribute, value = pair.flat_map(&:split) 
    hsh[name] = { attribute => value } 
end 

Dieser Weg gehen könnte.

Bearbeiten. Wenn wir mehr als ein Attribut unter der name whoever Linie definiert haben, konnten wir dies so etwas wie:

lines.slice_before(/name/).with_object({}) do |clump, hsh| 
    _key, name, *rest = *clump.flat_map(&:split) 
    hsh[name] = rest.each_slice(2).to_h 
end 
+0

Dies funktioniert auf Zweier-Paare, aber was ich wirklich suche, ist, für jedes Paar wie 'Alter 34' nach' NameCarl' innerhalb von Carl-Array, wenn 'Name Sean' alles danach erreicht wird Sean Array ... Irgendwelche Ideen? – greenbandit

+0

@greenbandit Ich habe eine zweite Lösung hinzugefügt. Dies sollte funktionieren, wenn unter jedem Namen mindestens ein Attribut vorhanden ist. –

0

Hier ist mein nehmen auf sie:

hash, current_hash = {}, {} 

lines.each do |line| 
    key, value = line.split(' ', 2) 
    if key == 'name' 
    hash[value] = current_hash = {} 
    else 
    current_hash[key] = value 
    end 
end 

hash ist das Ergebnis.

Ein anderer Weg, mit reduce:

current_hash = {} 

lines.reduce({}) do |hash, line| 
    key, value = line.split(' ', 2) 
    if key == 'name' 
    hash[value] = current_hash = {} 
    else 
    current_hash[key] = value 
    end 
    hash 
end 
0

ich aus einem Kommentar, dass die Anzahl von aufeinanderfolgenden Codezeilen, die unbekannt ist werden sollen, gruppiert. Angenommen, die Zeichenfolge waren wie folgt:

str = <<BITTER_END 
name carl 
age 34 
iq 95 
name sean 
age 15 
iq 166 
BITTER_END 
    #=> "name carl\nage 34\niq 95\nname sean\nage 15\niq 166\n" 

Der erste Schritt ist es, die Anzahl von Gruppen zu bestimmen. Wir können das tun, wie folgt:

first = str[/\w+/] 
    #=> "name" 
nbr_groups = str.scan(/\b#{first}\b/).size 
    #=> 2 

\b ist ein „Wortbruch“ matching zu vermeiden, zum Beispiel „umbenennen“.

Lassen Sie uns jetzt konvertieren str auf ein Array, und während wir gerade dabei sind, erhalten die lästigen Zeilenumbrüche befreien:

arr = str.lines.map(&:chomp!) 
    #=> ["name carl", "age 34", "iq 95", 
    # "name sean", "age 15", "iq 166"] 

Die Anzahl der Elemente pro Gruppe gleich:

arr.size/nbr_groups 
    #=> 3 

wir sind jetzt bewerben Enumerable#each_slice lesen:

enum = arr.each_slice(arr.size/nbr_groups) 
    #=> #<Enumerator: ["name carl", "age 34", "iq 95", "name sean", "age 15", 
    # "iq 166"]:each_slice(3)> 

wir con können Vert Dieses Enumerator auf einem Array, die (beiden) Elemente zu sehen, es generieren:

enum.to_a 
    #=> [["name carl", "age 34", "iq 95"], 
    # ["name sean", "age 15", "iq 166"]] 

wir nun die gewünschte Hash bilden kann.

enum.each_with_object({}) do |(key_str, *val_arr), h| 
    h[key_str.split.last] = val_arr.each_with_object({}) do |key_val_str, g| 
    key, value = key_val_str.split 
    g[key] = value 
    end 
end 
    #=> {"carl"=>{"age"=>"34", "iq"=>"95"}, "sean"=>{"age"=>"15", "iq"=>"166"}} 

Um zu sehen, was erste Note hier passiert, dass ein anderer enumerator erstellt wird:

enum1 = enum.each_with_object({}) 
    #=> #<Enumerator: #<Enumerator: ["name carl", "age 34", "iq 95", "name sean", 
    #  "age 15", "iq 166"]:each_slice(3)>:each_with_object({})> 
enum1.to_a 
    #=> [[["name carl", "age 34", "iq 95"], {}], 
    # [["name sean", "age 15", "iq 166"], {}]] 

Wenn Sie bei der ersten Rückgabewert einen genauen Blick haben, werden Sie sehen, dass enum1 gedacht werden kann als "zusammengesetzter Enumerator".

Das erste Element des enum1 zum Block übergeben wird und die Blockgrößen zu diesem Element zugeordnet sind, unter Verwendung von parallel Zuordnung (manchmal Mehrfachzuordnung):

(key_str, *val_arr), h = enum1.next 
    #=> [["name carl", "age 34", "iq 95"], {}] 
key_str 
    #=> "name carl" 
val_arr 
    #=> ["age 34", "iq 95"] 
h #=> {} 

der Berechnungsblock ist daher:

h["carl"] = ["age 34", "iq 95"].each_with_object({}) do |key_val_str, g| 
    key, value = key_val_str.split 
    g[key] = value 
end 
    #=> {"age"=>"34", "iq"=>"95"} 

Ich werde es dem Leser überlassen, zu decodieren die rechte Seite dieses Ausdrucks. sean bekommt die gleiche Behandlung (außer die Blockvariable h wird gleich {"carl"=>{"age"=>"34", "iq"=>"95"}}).

Putting dies zusammen, wir haben:

arr = str.lines.map(&:chomp!) 
arr.each_slice(arr.size/str.scan(/\b#{str[/\w+/]}\b/).size). 
    each_with_object({}) do |(key_str, *val_arr), h| 
     h[key_str.split.last] = val_arr.each_with_object({}) do |key_val_str, g| 
     key, value = key_val_str.split 
     g[key] = value 
    end 
end 
    #=> {"carl"=>{"age"=>"34", "iq"=>"95"}, "sean"=>{"age"=>"15", "iq"=>"166"}} 

obwohl es könnte klarer sein, einige der Zwischenvariablen zu halten.

1 Das geschieht durch Enumerator#each, die Array#each ruft, aber das ist eine andere Geschichte.

Verwandte Themen