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.
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
@greenbandit Ich habe eine zweite Lösung hinzugefügt. Dies sollte funktionieren, wenn unter jedem Namen mindestens ein Attribut vorhanden ist. –