In Ruby, how can I convert this string format to a hash -
the string format this
"a.b.c = 1" "a.b.d = 2" => hash => {'a'=> {'b' => {'c'=>1, 'd'=>2 } } } it gets tricky arrays
"a.e[0].f = 1" "a.e[0].g = 2" "a.e[1].h = 3" => hash => {'a' => {'e' => [{'f'=>1, 'g'=>2}, {'h'=>3}] } } i wrote version doesn't handle arrays many if-else checks
def construct $output[$words[0]] = {} unless $output.has_key?($words[0]) pointer = $output[$words[0]] $words[1..-2].each |word| pointer[word] = {} unless pointer.has_key?(word) pointer = pointer[word] end pointer[$words[-1]] = 1 end $output = {} $words = ['a', 'b', 'c'] construct $words = ['a', 'b', 'd'] construct p $output the array version worse. there better way of solving in ruby?
not simple did expect. here came with:
class hash def hash_merge(other) update(other) | key, val_self, val_other | case val_self when hash val_self.hash_merge val_other when array val_self += [nil] * (val_other.length - val_self.length) val_self.zip(val_other).map { | a, b | && b ? a.hash_merge(b) : || b } else # error end end end end # parses string of form "a.e[1].g = 2" hash {"a" => {"e" => [nil , {"g" => 2}]}} def construct_string(s) if s =~ /\a\s*(\s+)\s+=\s+(\d+)\z/ s1, s2 = $1, $2 s1.split('.').reverse.inject(s2.to_i) | hash, name | if name =~ /\[(\d+)\]\z/ name, idx = $~.pre_match, $1.to_i array = [] array[idx] = hash { name => array } else { name => hash } end end else # error end end # parses array of string , merges resulting hashes def construct(s) s.map { | e | construct_string(e) }.inject(&:hash_merge) end ins = ["a.e[0].f = 1", "a.e[0].g = 2", "a.e[1].h = 3"] p construct(ins) # {"a"=>{"e"=>[{"f"=>1, "g"=>2}, {"h"=>3}]}}
Comments
Post a Comment