简体   繁体   English

哈希初始化的Ruby哈希

[英]Ruby hash of hash initialization

Consider @data is an array of ActiveRecords with fields date , class , name , and grade . 考虑@data是ActiveRecords的数组,其字段为dateclassnamegrade Assuming I want to end up with two hashes, one being a unique set of all the dates per name; 假设我想以两个哈希结束,一个是每个名称中所有日期的唯一集合; the other broken down by class, then date, then name to reveal the grade. 另一个按班级细分,然后按日期,然后按名称显示成绩。

dates  = {}
grades = {}

@data.each do |d|
   dates[ d.name ][ d.date ] = ''                              # 1
   grades[ d.class => { d.date => { d.name => d.grade } } ]    # 2 
end
  1. results in an error: 导致错误:

    undefined method `[]=' for nil:NilClass 未定义的方法[[] ='for nil:NilClass

    dates[ d.name => { d.date => '' } ] only stores the last record (does not merge into the older results) dates[ d.name => { d.date => '' } ]仅存储最后一条记录(不会合并到较早的结果中)

  2. doesn't error, but ends with an empty hash {} 不会出错,但以空哈希{}结尾

After getting back into Perl, I've forgotten how to initialize and add to a hash of hashes in Ruby. 回到Perl之后,我忘记了如何在Ruby中初始化和添加到哈希哈希中。


Update 更新

For the first one ( dates ), it looks like I can do the following inside the loop: 对于第一个( 日期 ),看起来我可以在循环内执行以下操作:

dates[ d.name ] ||= { d.date => '' }
dates[ d.name ][ d.date ] = ''

Looking at the docs, I don't know if this is the proper way to do it, it also seems inefficient, since it will be doing the assignment twice for the first value. 查看文档,我不知道这是否是正确的方法,它似乎效率也不高,因为它将对第一个值进行两次赋值。 It also would suggest that for each key, I need to declare/initialize the underlying data structure, which would get lengthy the deeper the hash of hashes gets (like the grades hash). 它还建议对于每个键,我都需要声明/初始化基础数据结构,随着哈希值的散列值(如等级散列值)的增加,该数据结构将变得冗长。


Source: 资源:

[{ name: 'greg', class:'science', date:'jan', grade:'a' },
 { name: 'greg', class:'math'   , date:'jan', grade:'b' },
 { name: 'barb', class:'history', date:'jan', grade:'a' },
 { name: 'barb', class:'science', date:'feb', grade:'c' }]

dates would look something like: 日期看起来像:

{ 'greg' => { 'jan' => '' },
  'barb' => { 'jan' => '' ,
              'feb' => '' 
            }
}

grades would look something like: 成绩看起来像:

{ 'science' => { 'jan' => { 'greg' => 'a' } ,
                 'feb' => { 'barb' => 'c' } },
  'math'    => { 'jan' => { 'greg' => 'b' } },
  'history' => { 'jan' => { 'barb' => 'a' } }
}

This will actually work, as when you try to access the key from the hash, it not only returns the default but also sets it: 这实际上可以正常工作,因为当您尝试从哈希访问密钥时,它不仅返回默认值,还对其进行设置:

Hash.new do |h, k|
  h[k] = Hash.new do |hh, kk|
    hh[kk] = {}
  end
end

Update: 更新:

This might be a little cleaner albeit potentially more confusing for future developers than just checking if nil. 尽管对于未来的开发人员而言,可能比仅检查是否为nil更混乱,但这可能会更清洁一点。

dates = Hash.new { |hash, key| hash[key] = {} }
grades = Hash.new { |hash, key| hash[key] = dates.clone }

try with: 尝试:

d={}
c=Client.first
d[c.last_name]=[]
d[c.last_name][c.id]=//your value

d[c.last_name]=[], initialize an array of hash for c.last_name d [c.last_name] = [],为c.last_name初始化一个哈希数组

I was able to build the dates hash like this: 我能够像这样构建日期哈希:

arr = [{ name: 'greg', class:'science', date:'jan', grade:'a' },
       { name: 'greg', class:'math'   , date:'jan', grade:'b' },
       { name: 'barb', class:'history', date:'jan', grade:'a' },
       { name: 'barb', class:'science', date:'feb', grade:'c' }]


dates = arr.group_by { |x| x[:name] }.each_with_object({}) do |(k, v), hash|
  dates = v.map { |i| i[:date] }.uniq
  dates_hash = dates.each_with_object({}) { |date, h| h[date] = "" }
  hash[k] = dates_hash
end

puts dates

{"greg"=>{"jan"=>""}, "barb"=>{"jan"=>"", "feb"=>""}}

Code

To obtain the unique :date values for each :name , you could use the form of Hash.update (same as merge! ) that uses a hash to determine the values of keys are are contained in both hashes being merged: 要获取每个:name的唯一:date值,您可以使用Hash.update的形式(与merge!相同),该形式使用哈希确定键的值包含在要合并的两个哈希中:

def dates_by_name(arr)
  arr.each_with_object({}) { |g,h|
    h.update({g[:name]=>[g[:date]]}) {|_,ov,nv|
      ov.include?(nv.first) ? ov : ov + nv}}
end

There are many ways to organize the results for grades by class, date and name. 有许多方法可以按班级,日期和名称来组织成绩等级。 The following employs recursion: 以下采用递归:

def grade_by_class_date_name(arr, fields)
  fd = fields.dup
  f = fd.shift
  return arr.first[:grade] if f==:grade
  g = arr.each_with_object(Hash.new { |h,k| h[k] = [] }) { |e,g|
    g[e[f]] << e.reject { |k,_| k == f } }
  g.each_key { |k| g[k] = grade_by_class_date_name(g[k], fd) }
  g 
end          

Examples 例子

arr = [{ name: 'greg', class:'science', date:'jan', grade:'a' },
       { name: 'greg', class:'math'   , date:'jan', grade:'b' },
       { name: 'barb', class:'history', date:'jan', grade:'a' },
       { name: 'barb', class:'science', date:'feb', grade:'c' }]

dates_by_name(arr)
  #=> {"greg"=>["jan"], "barb"=>["jan", "feb"]} 

The hash returned by dates_by_name is not in the form you requested, but you could manipulate it easily to suit your requirements. dates_by_name返回的哈希值不是您要求的格式,但是您可以轻松地对其进行操作以满足您的要求。

fields = [:class, :date, :name, :grade] 

grade_by_class_date_name(arr, fields)
  #=> {"science"=>{"jan"=>{"greg"=>"a"},
  #                "feb"=>{"barb"=>"c"}},
  #    "math"   =>{"jan"=>{"greg"=>"b"}},
  #    "history"=>{"jan"=>{"barb"=>"a"}}} 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM