Serializing tabular data in ruby -- is map, flatten, hash the correct approach? I wanted a hash which lets me reference Japanese syllables by their romanized names. In hindsight I could have searched for an existing one column table, but I wanted to improve my ruby by writing a function which serializes these multi-column tables I found on wikipedia:

 katakana:
 v_eng:   a i u e o
 v_jap:   ア イ ウ エ オ
 K:   カ キ ク ケ コ
 S:   サ シ ス セ ソ
 T:   タ チ ツ テ ト
 N:   ナ ニ ヌ ネ ノ
 H:   ハ ヒ フ ヘ ホ
 M:   マ ミ ム メ モ
 Y:   ヤ _ ユ _ ヨ
 R:   ラ リ ル レ ロ
 W:   ワ ヰ _ ヱ ヲ
hiragana:
 v_eng:   a i u e o
 v_jap:   あ い う え お
 k:   か き く け こ
 s:   さ し す せ そ
 t:   た ち つ て と
 n:   な に ぬ ね の
 h:   は ひ ふ へ ほ
 m:   ま み む め も
 y:   や _ ゆ _ よ
 r:   ら り る れ ろ
 w:   わ ゐ _ ゑ を
 nn:   ん _ _ _ _


I was able to create the serializing function, syllabarys():

#!/usr/bin/env ruby
require 'yaml'

def syllabarys
@syllabarys ||= lambda{
   raw_data = YAML.load_file 'japanese.dic'

   syllabary_names = ['katakana','hiragana']

   a = syllabary_names.map{|syllabary|

      syllabary_data = raw_data[syllabary]

      veng,vjap = syllabary_data['v_eng'].split, syllabary_data['v_jap'].split

      vowels = Hash[*veng.zip(vjap).flatten] #zipped flat array =&gt; splat

      #jp row strings by en consonants:
      jrsbec = syllabary_data.select{|con,row|con =~ /^[KSTNHMYRWkstnhmyrwN]$/}

      #jp row arrays by en consonants:
      jrabec = Hash[*jrsbec.map{|con,row|[con,row.split]}.flatten(1)]

      #en vowels with jp row arrays by en consonants:
      evwjrabec = Hash[jrabec.map{|con,row|[con,Hash[veng.zip(row)]]}] #array of hashes =&gt; no splat

      #jp syllables by en syllables: 
         #outer map provides en consonant to inner map
         #inner map creates the dictionary we want in array form, e.g. [#K#[['Ka','カ'],..], #S..]
         #flatten(1) removes outer array created by outer map [['Ka','カ'],..] =&gt; no splat
      jp_by_en = Hash[evwjrabec.map{|con,row|row.map{|vowel,jp_syl| [con+vowel,jp_syl] }}.flatten(1)]

      #remove forgotten syllables:
      jp_by_en.select{|en_syl,jp_syl|jp_syl != '_'}
   }
   Hash[*syllabary_names.zip(a).flatten(1)]
}.call
end


And it returns the desired hash:

{"katakana"=&gt;{"Ka"=&gt;"カ", "Ki"=&gt;"キ", "Ku"=&gt;"ク", "Ke"=&gt;"ケ", "Ko"=&gt;"コ", "Sa"=&gt;"サ", "Si"=&gt;"シ", "Su"=&gt;"ス", "Se"=&gt;"セ", "So"=&gt;"ソ", "Ta"=&gt;"タ", "Ti"=&gt;"チ", "Tu"=&gt;"ツ", "Te"=&gt;"テ", "To"=&gt;"ト", "Na"=&gt;"ナ", "Ni"=&gt;"ニ", "Nu"=&gt;"ヌ", "Ne"=&gt;"ネ", "No"=&gt;"ノ", "Ha"=&gt;"ハ", "Hi"=&gt;"ヒ", "Hu"=&gt;"フ", "He"=&gt;"ヘ", "Ho"=&gt;"ホ", "Ma"=&gt;"マ", "Mi"=&gt;"ミ", "Mu"=&gt;"ム", "Me"=&gt;"メ", "Mo"=&gt;"モ", "Ya"=&gt;"ヤ", "Yu"=&gt;"ユ", "Yo"=&gt;"ヨ", "Ra"=&gt;"ラ", "Ri"=&gt;"リ", "Ru"=&gt;"ル", "Re"=&gt;"レ", "Ro"=&gt;"ロ", "Wa"=&gt;"ワ", "Wi"=&gt;"ヰ", "We"=&gt;"ヱ", "Wo"=&gt;"ヲ"}, "hiragana"=&gt;{"ka"=&gt;"か", "ki"=&gt;"き", "ku"=&gt;"く", "ke"=&gt;"け", "ko"=&gt;"こ", "sa"=&gt;"さ", "si"=&gt;"し", "su"=&gt;"す", "se"=&gt;"せ", "so"=&gt;"そ", "ta"=&gt;"た", "ti"=&gt;"ち", "tu"=&gt;"つ", "te"=&gt;"て", "to"=&gt;"と", "na"=&gt;"な", "ni"=&gt;"に", "nu"=&gt;"ぬ", "ne"=&gt;"ね", "no"=&gt;"の", "ha"=&gt;"は", "hi"=&gt;"ひ", "hu"=&gt;"ふ", "he"=&gt;"へ", "ho"=&gt;"ほ", "ma"=&gt;"ま", "mi"=&gt;"み", "mu"=&gt;"む", "me"=&gt;"め", "mo"=&gt;"も", "ya"=&gt;"や", "yu"=&gt;"ゆ", "yo"=&gt;"よ", "ra"=&gt;"ら", "ri"=&gt;"り", "ru"=&gt;"る", "re"=&gt;"れ", "ro"=&gt;"ろ", "wa"=&gt;"わ", "wi"=&gt;"ゐ", "we"=&gt;"ゑ", "wo"=&gt;"を"}}


This task was a good exercise in index-free coding, but I notice my code exhibits a recurring pattern of map|zip->flatten->Hash. I'd like to know if this is this a normal pattern in ruby or if there's a better way of serializing tabular data.
Feedback:


Instead of ||= lambda { ... }.call, you can use ||= begin ... end
Instead of Hash[*arr] you can use arr.to_h in Ruby 2.0+
You don't need to convert everything to a hash if you just want to use it for .map later -- [[1, 2], [3, 4]].map { |k, v| k + v } #=> [3, 7]
Instead of .map { ... }.flatten(1), you can use .flat_map { ... }
If you won't be using a variable in a block, you can use _ instead, like .map { |key, _| key }


I rewrote the code to be like what I'd code it today.

require 'yaml'

def do_it(raw)
  map = raw["v_eng"].split.zip(raw["v_jap"].split)

  raw.select do |k, _|
    k.size == 1
  end.flat_map do |pre, japs|
    map.zip(japs.split).map do |(post, _), jap|
      [pre + post, jap] unless jap == '_'
    end.compact
  end.to_h
end

want = {"katakana"=&gt;{"Ka"=&gt;"カ", "Ki"=&gt;"キ", "Ku"=&gt;"ク", "Ke"=&gt;"ケ", "Ko"=&gt;"コ", "Sa"=&gt;"サ", "Si"=&gt;"シ", "Su"=&gt;"ス", "Se"=&gt;"セ", "So"=&gt;"ソ", "Ta"=&gt;"タ", "Ti"=&gt;"チ", "Tu"=&gt;"ツ", "Te"=&gt;"テ", "To"=&gt;"ト", "Na"=&gt;"ナ", "Ni"=&gt;"ニ", "Nu"=&gt;"ヌ", "Ne"=&gt;"ネ", "No"=&gt;"ノ", "Ha"=&gt;"ハ", "Hi"=&gt;"ヒ", "Hu"=&gt;"フ", "He"=&gt;"ヘ", "Ho"=&gt;"ホ", "Ma"=&gt;"マ", "Mi"=&gt;"ミ", "Mu"=&gt;"ム", "Me"=&gt;"メ", "Mo"=&gt;"モ", "Ya"=&gt;"ヤ", "Yu"=&gt;"ユ", "Yo"=&gt;"ヨ", "Ra"=&gt;"ラ", "Ri"=&gt;"リ", "Ru"=&gt;"ル", "Re"=&gt;"レ", "Ro"=&gt;"ロ", "Wa"=&gt;"ワ", "Wi"=&gt;"ヰ", "We"=&gt;"ヱ", "Wo"=&gt;"ヲ"}, "hiragana"=&gt;{"ka"=&gt;"か", "ki"=&gt;"き", "ku"=&gt;"く", "ke"=&gt;"け", "ko"=&gt;"こ", "sa"=&gt;"さ", "si"=&gt;"し", "su"=&gt;"す", "se"=&gt;"せ", "so"=&gt;"そ", "ta"=&gt;"た", "ti"=&gt;"ち", "tu"=&gt;"つ", "te"=&gt;"て", "to"=&gt;"と", "na"=&gt;"な", "ni"=&gt;"に", "nu"=&gt;"ぬ", "ne"=&gt;"ね", "no"=&gt;"の", "ha"=&gt;"は", "hi"=&gt;"ひ", "hu"=&gt;"ふ", "he"=&gt;"へ", "ho"=&gt;"ほ", "ma"=&gt;"ま", "mi"=&gt;"み", "mu"=&gt;"む", "me"=&gt;"め", "mo"=&gt;"も", "ya"=&gt;"や", "yu"=&gt;"ゆ", "yo"=&gt;"よ", "ra"=&gt;"ら", "ri"=&gt;"り", "ru"=&gt;"る", "re"=&gt;"れ", "ro"=&gt;"ろ", "wa"=&gt;"わ", "wi"=&gt;"ゐ", "we"=&gt;"ゑ", "wo"=&gt;"を"}}

raw = YAML.load_file 'japanese.dic'

p do_it(raw["katakana"]) == want["katakana"] #=&gt; true
p do_it(raw["hiragana"]) == want["hiragana"] #=&gt; true


Hope that helps. Let me know if you want any other clarification(s) in a comment below.