bekommen Dies ist ein weiterer Weg, dass weniger Daten benötigt um den Hash zu erstellen. Wenn zum Beispiel die Linie
A = B = C = D
vorhanden ist, gibt es keine Notwendigkeit für eine der folgenden:
A = B
A = B = C
und die Reihenfolge der Linien unwichtig ist.
-Code
def hashify(str)
str.lines.each_with_object({}) { |line, h|
line.split(/\s*=\s*/).reduce(h) { |g,w|
(w[-1] == "\n") ? g[w.chomp] = nil : g[w] ||= {} } }
end
Beispiel
str =<<_
A = B = C
G = I
A = B = D
A = E = F
G = H
A = K
G = J
_
hashify(str)
#=> {"A"=>{"B"=>{"C"=>nil, "D"=>nil}, "E"=>{"F"=>nil}, "K"=>nil},
# "G"=>{"I"=>nil, "H"=>nil, "J"=>nil}}
Erklärung
Für str
oben:
a = str.lines
#=> ["A = B = C\n", "A = B = D\n", "A = E = F\n",
# "G = H\n", "G = I\n", "G = J\n"]
Beachten Sie, dass String#lines im Gegensatz zu split(/'\n'/)
die Newline-Zeichen behält. Sie an dieser Stelle zu halten war beabsichtigt; sie dienen einem wichtigen Zweck, wie unten gezeigt wird.
enum = a.each_with_object({})
#=> #<Enumerator: ["A = B = C\n", "A = B = D\n", "A = E = F\n", "G = H\n",
# "G = I\n", "G = J\n"]:each_with_object({})>
wir den Enumerator auf einem Array konvertieren, die Elemente zu sehen, die Array#each zum Block übergeben wird:
enum.to_a
#=> [["A = B = C\n", {}], ["A = B = D\n", {}], ["A = E = F\n", {}],
# ["G = H\n", {}], ["G = I\n", {}], ["G = J\n", {}]]
enum
ruft nun each
jedes Element in den Block passieren:
enum.each { |line, h| line.split(/\s*=\s*/).reduce(h) { |g,w|
(w[-1] == '\n') ? g[w.chomp] = nil : g[w] ||= {} } }
#=> {"A"=>{"B"=>{"C\n"=>{}, "D\n"=>{}}, "E"=>{"F\n"=>{}}},
# "G"=>{"H\n"=>{}, "I\n"=>{}, "J\n"=>{}}}
Der erste Wert, der Array#each
in den Block übergibt, lautet:
["A = B = C\n", {}]
die zerlegt wird, oder "disambiguiert" in seinen zwei Elemente und den Blockvariablen zugewiesen:
line = "A = B = C\n"
h = {}
wir jetzt den Code im Block auszuführen:
b = line.split(/\s*=\s*/)
#=> ["A", "B", "C\n"]
b.reduce(h) { |g,w|
(w[-1] == '\n') ? g[w.chomp] = nil : g[w] ||= {} }
#=> {}
Der Anfangswert für reduce
ist der Hash h
, den wir erstellen, der zunächst leer ist.Wenn h
und "A"
werden in den Block übergeben,
g = h #=> {}
w = "A"
so (unter Hinweis darauf, dass doppelte Anführungszeichen für "\n"
benötigt werden)
w[-1] == "\n"
#=> "A" == '\n'
#=> false
so führen wir
g[w] ||= {}
#=> g['A'] ||= {}
#=> g['A'] = g['A'] || {}
#=> g['A'] = nil || {}
#=> {}
so jetzt
h #=> {"A"=>{}}
g[w] => {}
wird dann an reduce
zurück zurück übergeben und die Blockgrößen für das zweite Element zu dem Block übergeben sind:
g = g["A"] #=> {}
w = "B"
Da
w[-1] == "\n" #=> false
wir wieder
g[w] ||= {}
#=> g["B"] ||=> {} => {}
ausführen und jetzt
h #=> {"A"=>{"B"=>{}}}
Schließlich [g["B"], "C\n"]
in den Block geleitet, zerlegt und auf den Block Variablen zugewiesen:
g = g["B"] #=> {}
w = "C\n"
aber die Anwesenheit der Newline-Zeichen in w
Ergebnissen in
w[-1] == "\n" #=> true
uns sagen, es ist das letzte Wort in der Zeile, so müssen wir die Zeilenende-Zeichen und den Wert auf nil
abzustreifen:
g[w.chomp] = nil
#=> g["C"] = nil
in resultierende:
h #=> {"A"=>{"B"=>{"C"=>nil}}}
den Zeichen newline Ende in der Zeichenfolge bereitgestellt, um die benötigten „Flag“ für die Verarbeitung des letzten Wortes in jeder Zeile anders als die anderen.
Die anderen Zeilen werden ähnlich verarbeitet.
Dank für die Antwort. Ich werde stattdessen versuchen, null zu bekommen. –
Siehe Update für wie Blätter zunichte zu machen. – mudasobwa
Danke @mudasobwa –