Arrays 在向数组中添加项时,迭代嵌套哈希以创建数组

Arrays 在向数组中添加项时,迭代嵌套哈希以创建数组,arrays,ruby,hash,Arrays,Ruby,Hash,我有一个嵌套哈希,我想重新排列键/值对。下面的示例显示了指向语言散列的样式散列,然后指向它所属语言类型的散列。我想重新格式化它,使其看起来像new\u hash示例。我理解通过在不同级别上迭代散列并创建散列来构造它,但是,我关心/困惑的部分是创建:style指向的数组,然后将正确的样式推送到它 我假设代码片段会像我期望的那样工作。我的new_hash将有一个指向另一个hash的:language键。这个散列有一个键:style,它指向一个数组,我将在其中存储与每种语言相关联的所有样式。:java

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

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


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

  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
new={}
语言。每个do |风格,编程|语言|
编程语言。每个do语言,类型|
键入.每个do |键、值|
新的[语言]={:type=>nil,:style=>[]}
结束
结束
结束
语言。每个do |风格,编程|语言|
编程语言。每个do语言,类型|
键入.每个do |键、值|
新建[语言][:类型]=值

new[language][:style]
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

一旦我们给散列起了更好的名字,它就变得更容易计算了。我还利用了,所以我们不必担心重复

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
请注意,我并没有对哈希和子哈希的初始化进行硬编码,而是在循环的每一级进行了硬编码。这意味着我不必列出所有的键,它将处理新的和意外的键


通过对值使用集合,我对一点语言信息可以包含多少值不做任何假设。

您可以使用(aka
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]}} 

您在
散列中缺少一个大括号。这是一个很好的例子,说明了为什么应该将右大括号放在自己的行上。你可以在一个循环中完成。将内部循环合并在一起,并在添加值之前初始化。您还将多次添加
:style
,它需要位于
type
循环之外。它之所以能工作,是因为
类型中只有一个键。始终是创造性的,而且我喜欢
|uo,|
看起来像块参数的方式。有点让我笑了
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
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]}}