迭代嵌套哈希以在向其添加项目时创建数组

时间:2017-01-05 19:51:06

标签: arrays ruby hash

我有一个嵌套的哈希,我想重新排列键/值对。下面的示例显示了指向语言哈希的样式哈希值,然后指向它所使用的语言类型的哈希值。我想将其重新格式化为new_hash示例。我理解通过在不同级别迭代哈希并创建类似的哈希来构造它,但是,我关注/困惑的部分是创建:style指向的数组然后推送正确的它的风格。

我认为代码段会像我期望的那样工作。我的new_hash将有一个:language的键,指向另一个哈希值。这个哈希有一个:style的键,指向一个数组,我将在其中存储与每种语言相关的所有样式。 :javascript哈希在其数组中应该有两个样式,因为它在原始hash中存在两次,但是,在运行此代码片段时,该数组不会添加这两种样式。似乎在分配哈希时的一次迭代中,:javascript被赋予:oo的样式,但在另一次迭代中,它被:functional替换。 我不确定初始化数组的语法,并在迭代哈希时为其添加多个项目。

hash = {
    :oo => {
        :ruby => {:type => "Interpreted"}, 
        :javascript => {:type => "Interpreted"},
    },
    :functional => {
        :scala => {:type => "Compiled"}, 
        :javascript => {:type => "Interpreted"}
    }
}

new_hash = {
    :ruby => {
        :type => "Interpreted", :style => [:oo]
    },
    :javascript => {
        :type => "Interpreted", :style => [:oo, :functional]
    },
    :scala => {
        :type => "Compiled", :style => [:functional]
    }
}

hash.each do |style, programming_language|
    programming_language.each do |language, type|
        type.each do |key, value|
            new_hash[language] = {:style => [style]}
        end 
    end
end

4 个答案:

答案 0 :(得分:2)

您可以使用Hash#update(aka merge!)和Hash#merge的形式使用哈希来确定合并的两个哈希中存在的键的值。有关详细信息,请参阅文档。

hash.each_with_object({}) do |(style,language_to_type_hash),h|
  language_to_type_hash.each do |language,type_hash|
    h.update(language=> { type: type_hash[:type], style: [style] }) do |_,o,_|
      o.merge(style: [style]) { |_,ostyle_arr,nstyle_arr| ostyle_arr + nstyle_arr }
    end
  end
end
  #=> {:ruby      =>{:type=>"Interpreted", :style=>[:oo]},
  #    :javascript=>{:type=>"Interpreted", :style=>[:oo, :functional]},
  #    :scala     =>{:type=>"Compiled",    :style=>[:functional]}} 

答案 1 :(得分:1)

Hash::new允许您为不存在的键指定默认值,因此在您的情况下,默认值为{type: nil, style: []}

此功能允许您只循环一次并按如下方式实现

programming_languages =  {
  :oo => {
    :ruby => {:type => "Interpreted"}, 
    :javascript => {:type => "Interpreted"},
  },
  :functional => {
    :scala => {:type => "Compiled"}, 
    :javascript => {:type => "Interpreted"}
  }
}



programming_languages.each_with_object(Hash.new {|h,k| h[k] = {type: nil, style: []}}) do |(style,languages),obj|
  languages.each do |language,type_hash|
    obj[language][:style] << style
    obj[language][:type] = type_hash[:type]
  end
end

输出:

#=> {:ruby=>{:type=>"Interpreted", :style=>[:oo]},
     :javascript=>{:type=>"Interpreted", :style=>[:oo, :functional]},
     :scala=>{:type=>"Compiled", :style=>[:functional]}}

答案 2 :(得分:0)

意识到这可以通过迭代迭代两次来解决。一旦初始化数组,第二次再添加必要的项目。虽然不确定这是否只能迭代哈希一次。

  new = {}
  languages.each do |style, programming_language|
    programming_language.each do |language, type|
      type.each do |key, value|
        new[language] = {:type => nil , :style => []}
      end 
    end
  end 
  languages.each do |style, programming_language|
    programming_language.each do |language, type|
      type.each do |key, value|
        new[language][:type] = value
        new[language][:style] << style
      end 
    end
  end 
  new

答案 3 :(得分:0)

一旦我们给哈希更好的名字,它就会变得容易一些。我也使用了sets,因此我们不必担心重复。

require 'set'

# Our new hash of language info. _new to differentiate between
# the hash of languages under the hash of styles.
languages_new = {}

# For each style...
styles.each do |style, languages|
    # For each language in that style...
    languages.each do |language, info|
        # Add a new hash for that language if there isn't one already
        languages_new[language] ||= {}

        # For each bit of info about that language...
        info.each do |key, val|
            # Add a new set for that info if there isn't one already
            # The `var = hash[key] ||= new_var` pattern allows
            # conditional initialization while also using either the
            # new or existing set.
            set = languages_new[language][key] ||= Set.new

            # Add the info to it
            set.add(val)
        end

        # Handle the special case of style.
        set = languages_new[language][:style] ||= Set.new
        set.add(style)
    end
end

请注意,我没有对哈希和子哈希的初始化进行硬编码,而是在每个循环级别中完成了它。这意味着我不必列出所有密钥,它将处理新的和意外的密钥。

通过对值使用集合,我不会假设一些语言信息可以有多少值。