6

現在、次のRubyハッシュをキックバックするサーバー呼び出しがあります。

{
  "id"=>"-ct",
  "factualId"=>"",
  "outOfBusiness"=>false,
  "publishedAt"=>"2012-03-09 11:02:01",
  "general"=>{
    "name"=>"A Cote",
    "timeZone"=>"EST",
    "desc"=>"À Côté is a small-plates restaurant in Oakland's charming
            Rockridge district. Cozy tables surround large communal tables in both
            the main dining room and on the sunny patio to create a festive atmosphere.
              Small plates reflecting the best of seasonal Mediterranean cuisine are served
            family-style by a friendly and knowledgeable staff.\nMenu items are paired with
            a carefully chosen selection of over 40 wines by the glass as well as a highly
            diverse bottled wine menu. Specialty drinks featuring fresh fruits, rare
            botaniques and fine liqueurs are featured at the bar.",
    "website"=>"http://acoterestaurant.com/"
  },
  "location"=>{
    "address1"=>"5478 College Ave",
    "address2"=>"",
    "city"=>"Oakland",
    "region"=>"CA",
    "country"=>"US",
    "postcode"=>"94618",
    "longitude"=>37.84235,
    "latitude"=>-122.25222
  },
  "phones"=>{
    "main"=>"510-655-6469",
    "fax"=>nil
  },
  "hours"=>{
    "mon"=>{"start"=>"", "end"=>""},
    "tue"=>{"start"=>"", "end"=>""},
    "wed"=>{"start"=>"", "end"=>""},
    "thu"=>{"start"=>"", "end"=>""},
    "fri"=>{"start"=>"", "end"=>""},
    "sat"=>{"start"=>"", "end"=>""},
    "sun"=>{"start"=>"", "end"=>""},
    "holidaySchedule"=>""
  },
  "businessType"=>"Restaurant"
}

次のように、ネストされたいくつかの属性があります。

"wed"=>{"start"=>"", "end"=>""}

このオブジェクトをRubyでネストされていないハッシュに変換する必要があります。理想的には、属性がネストされているかどうかを検出し、それに応じて応答したいと思います。IEは、属性' wed'がネストされていると判断した場合、データを取り出してフィールド' wed-start'と''などに格納しwed-endます。

誰かが始める方法について何かヒントがありますか?

4

4 に答える 4

10

編集:sparsify gemは、この問題の一般的な解決策としてリリースされました。


これが私が数ヶ月前に作成した実装です。JSONをハッシュに解析してから、Sparsifyを使用してハッシュを解析する必要があります。

# Extend into a hash to provide sparse and unsparse methods. 
# 
# {'foo'=>{'bar'=>'bingo'}}.sparse #=> {'foo.bar'=>'bingo'}
# {'foo.bar'=>'bingo'}.unsparse => {'foo'=>{'bar'=>'bingo'}}
# 
module Sparsify
  def sparse(options={})
    self.map do |k,v|
      prefix = (options.fetch(:prefix,[])+[k])
      next Sparsify::sparse( v, options.merge(:prefix => prefix ) ) if v.is_a? Hash
      { prefix.join(options.fetch( :separator, '.') ) => v}
    end.reduce(:merge) || Hash.new
  end
  def sparse!
    self.replace(sparse)
  end

  def unsparse(options={})
    ret = Hash.new
    sparse.each do |k,v|
      current = ret
      key = k.to_s.split( options.fetch( :separator, '.') )
      current = (current[key.shift] ||= Hash.new) until (key.size<=1)
      current[key.first] = v
    end
    return ret
  end
  def unsparse!(options={})
    self.replace(unsparse)
  end

  def self.sparse(hsh,options={})
    hsh.dup.extend(self).sparse(options)
  end

  def self.unsparse(hsh,options={})
    hsh.dup.extend(self).unsparse(options)
  end

  def self.extended(base)
    raise ArgumentError, "<#{base.inspect}> must be a Hash" unless base.is_a? Hash
  end
end

利用方法:

external_data = JSON.decode( external_json )
flattened = Sparsify.sparse( external_data, :separator => '-' )

これは元々、Mongoに一連のものを格納する作業を行っていたために作成されました。これにより、更新時にスパースキー(ドット区切り)を使用して、無関係のキーを上書きせずにネストされたハッシュの一部のコンテンツを更新できました。

于 2012-08-22T00:24:11.433 に答える
6

これが完全なソリューションの最初のカットです。もっとエレガントに書けると思いますが、かなりはっきりしているようです。これをRubyファイルに保存して実行すると、以下に示す出力が得られます。

class Hash
  def unnest
    new_hash = {}
    each do |key,val|
      if val.is_a?(Hash)
        new_hash.merge!(val.prefix_keys("#{key}-"))
      else
        new_hash[key] = val
      end
    end
    new_hash
  end

  def prefix_keys(prefix)
    Hash[map{|key,val| [prefix + key, val]}].unnest
  end
end

p ({"a" => 2, "f" => 5}).unnest
p ({"a" => {"b" => 3}, "f" => 5}).unnest
p ({"a" => {"b" => {"c" => 4}, "f" => 5}}).unnest

出力:

{"a"=>2, "f"=>5}
{"a-b"=>3, "f"=>5}
{"a-b-c"=>4, "a-f"=>5}
于 2012-08-22T00:14:41.430 に答える
4

もう1つのオプション:

class Hash
  def smash(prefix = nil)
    inject({}) do |acc, (k, v)|
      key = prefix.to_s + k
      if Hash === v
        acc.merge(v.smash(key + '-'))
      else
        acc.merge(key => v)
      end
    end
  end
end

hash = {
  'f' => 100,
  'z' => {'j' => 25},
  'a' => {'b' => {'c' => 1}}
}

puts hash.smash # => {"f"=>100, "z-j"=>25, "a-b-c"=>1}
于 2012-08-22T05:42:41.467 に答える
2

これに取り組む別の方法は、ハッシュをフラット化するのではなく、フラット化されたかのようにアクセスすることです。たとえば、次のハッシュが与えられます。

h = {
  'a' => 1,
  'b' => {
    'c' => 2,
    'd' => 3,
  },
}

次に、この関数:

NESTED_KEY_SEPARATOR = '-'
NESTED_KEY_REGEX = /^(.*?)(?:#{NESTED_KEY_SEPARATOR}(.*))?$/

def nested_fetch(key, hash)
  return hash if key.empty?
  first_part_of_key, rest_of_key = NESTED_KEY_REGEX.match(key).captures
  value = hash[first_part_of_key]
  if value.is_a?(Hash)
    nested_hash_fetch(value, rest_of_key || '')
  elsif rest_of_key
    nil
  else
    value
  end
end

個々のハッシュキーをKEY_SEPARATORと連結することにより、ネストされたハッシュ要素を取得できます(ここではダッシュに設定されていますが、検索する必要のあるハッシュでキーとして表示されない任意の文字である可能性があります)。

p nested_fetch('a', h)      # => 1
p nested_fetch('b-c', h)    # => 2

部分的に修飾されたキーを指定すると、その時点で一致したハッシュが取得されます。

p nested_fetch('b', h)      # => {"c"=>2, "d"=>3}

そして、存在しないキーを与えると、nilになります。

p nested_fetch('b-x', h)    # => nil

これは、必要に応じて、上記のコードをクラスHashで囲むだけで、引数ハッシュのデフォルトとしてselfを指定することにより、Hashにモンキーパッチを適用できます。

class Hash
  NESTED_KEY_SEPARATOR = '-'
  NESTED_KEY_REGEX = /^(.*?)(?:#{KEY_SEPARATOR}(.*))?$/

  def nested_fetch(key, hash = self)
  ...
end
于 2012-08-22T00:51:28.527 に答える