上の図のようにスプレッドシートで入力して、それを読んで 次のようにテキストファイルに出力したい。 *1
{"a":111,"b":{"d":121,"e":122},"c":{"f":{"h":131,"i":132},"g":141}} {"a":211,"b":{"d":221,"e":222},"c":{"f":{"h":231,"i":232},"g":241}}
スプレッドシートから読んで前処理した時点で次のようになっているとして、 これをもとに組み立てる処理を書く 。 *2
# 項目のキーの情報 [ [ "a", ], [ "b", "d" ], [ "b", "e" ], [ "c", "f", "h" ], [ "c", "f", "i" ], [ "c", "g" ] ] # 各レコード [ [111, 121, 122, 131, 132, 141], [211, 221, 222, 231, 232, 241] ]
適当に書いたもの。
# convert.rb def set_value(obj, item_name, value) current = obj keys = item_name.dup while keys.size >= 2 do key = keys.shift() current[key] ||= {} current = current[key] end key = keys.shift() current[key] = value obj end def convert(item_names, values) obj = {} item_names.zip(values) .each do |item_name, value| obj = set_value(obj, item_name, value) end obj end # -------------------------------- require "yaml" item_names = [ [ "a", ], [ "b", "d" ], [ "b", "e" ], [ "c", "f", "h" ], [ "c", "f", "i" ], [ "c", "g" ] ] rows = [ [111, 121, 122, 131, 132, 141], [211, 221, 222, 231, 232, 241] ] rows.each do |values| obj = convert(item_names, values) puts YAML.dump(obj) end
結果:
$ ruby convert.rb --- a: 111 b: d: 121 e: 122 c: f: h: 131 i: 132 g: 141 --- a: 211 b: d: 221 e: 222 c: f: h: 231 i: 232 g: 241
項目ごとの処理をくりかえす部分は Enumerable#inject
を使って書き直せる。
def convert_v2(item_names, values) item_names.zip(values) .inject({}) do |obj, item_name_value| set_value(obj, *item_name_value) end end
扱っている対象が木なので、再帰で書くと良さそう?
def set_value_v2(parent, item_name, value) parent ||= {} key, *rest_keys = item_name parent[key] = if rest_keys.empty? value else set_value_v2(parent[key], rest_keys, value) end parent end