尝试从几个哈希值创建深度为3的哈希

时间:2017-10-23 20:58:14

标签: ruby recursion hash tree refactoring

我正在尝试使用哈希数组,其中每个哈希值都具有多个键,值的深度,并尝试创建包含所有这些数据的新哈希。

我的数据:

a = {'name' => 200, 'segment' => 'alpha',  'dc' => 'nyc', 'designation' => 'web'}
b = {'name' => 201, 'segment' => 'shared', 'dc' => 'nyc', 'designation' => 'generic'}
c = {'name' => 202, 'segment' => 'alpha', 'dc' => 'nyc', 'designation' => 'app'}
d = {'name' => 400, 'segment' => 'alpha',  'dc' => 'sfc', 'designation' => 'web'}
e = {'name' => 401, 'segment' => 'shared', 'dc' => 'sfc', 'designation' => 'web'}
f = {'name' => 402, 'segment' => 'shared', 'dc' => 'sfc', 'designation' => 'app'}
g = {'name' => 403, 'segment' => 'alpha', 'dc' => 'sfc', 'designation' => 'app'}

members = [a,b,c,d,e,f,g]

这是我期待得到的:

{
  :alpha => {
    :nyc => {
      :web => [200],
      :app => [202]
    },
    :sfc => {
      :web => [400],
      :app => [403],
    }
  },
  :shared => {
    :nyc => {
       generic => [201]
    },
    :sfc => {
      :app => [402],
      :web => [401]
    }
  }
}

这是我的代码(至少......它的开头......)

members.each do |m|
  if m.key? 'segment'
    dict[m['segment']] = {} unless dict.key? m['segment']
  end
  puts m['dc']
  if m.key? 'dc'
    dict[m['segment']] = m['dc']
    #dict[m['segment']['dc']] = m['segment']['dc']# unless dict[m['segment']].key? m['dc']
  end

我尝试了各种各样的代码和我得到的结果

{"alpha"=>"sfc", "shared"=>"sfc"}
{"alpha"=>{"sfc"=>{}}, "shared"=>{"sfc"=>{}}}
{"alpha"=>{"sfc"=>{}}, nil=>{"web"=>[]}, "shared"=>{"sfc"=>{}}}

我需要搜索什么流行语来解决这个问题?

谢谢,

2 个答案:

答案 0 :(得分:2)

members.group_by { |g| g['segment'] }.
        transform_values { |a| a.group_by { |f| f['dc'] }.
          transform_values { |aa| aa.group_by { |h| h['designation'] }.
            transform_values { |aaa| aaa.map { |e| e['name'] } } } }
  #=> { "alpha"=>{
          "nyc"=>{
             "web"=>[200],
             "app"=>[202]
          },
          "sfc"=>{
             "web"=>[400],
             "app"=>[403]
          }
        },
        "shared"=>{
          "nyc"=>{
            "generic"=>[201]
          },
          "sfc"=>{
            "web"=>[401],
            "app"=>[402]
          }
        }
      }

请参阅Enumerble#group_byHash#transform_values的文档。后者在Ruby v2.4中首次亮相。

对于2.4之前的Ruby版本,可以很容易地创建方法Hash#tranform_values

class Hash
  def transform_values
    Hash[map { |k,v| [k, yield(v)] }]
  end
end

然后,例如,

{ a: 1, b: 3 }.transform_values { |v| 2*v }
  #=> { :a=>2, :b=>6 }

答案 1 :(得分:1)

在Ruby 2.3和2.0中(没有transform_valuesgroup_by):

a = {'name' => 200, 'segment' => 'alpha',  'dc' => 'nyc', 'designation' => 'web'}
b = {'name' => 201, 'segment' => 'shared', 'dc' => 'nyc', 'designation' => 'generic'}
c = {'name' => 202, 'segment' => 'alpha', 'dc' => 'nyc', 'designation' => 'app'}
d = {'name' => 400, 'segment' => 'alpha',  'dc' => 'sfc', 'designation' => 'web'}
e = {'name' => 401, 'segment' => 'shared', 'dc' => 'sfc', 'designation' => 'web'}
f = {'name' => 402, 'segment' => 'shared', 'dc' => 'sfc', 'designation' => 'app'}
g = {'name' => 403, 'segment' => 'alpha', 'dc' => 'sfc', 'designation' => 'app'}

members = [a,b,c,d,e,f,g]

h1 = members.group_by { | gr | gr['segment'] }
p h1
#           in h1 at key k1 put this new value
h1.each { | k1, v1 | h1[k1] = v1.group_by { | gr | gr['dc'] } }
puts
p h1
h1.each { | k1, v1 | v1.each { | k2, v2 | v1[k2] = v2.group_by { | gr | gr['designation'] } } }
puts
p h1
h1.each { | k1, v1 | v1.each { | k2, v2 | v2.each { | k3, v3 | v2[k3] = v3.collect { | el | el['name'] } } } }
puts '--- final result ---'
p h1

也许在Ruby 1.8中(没有group_by):

a = {'name' => 200, 'segment' => 'alpha',  'dc' => 'nyc', 'designation' => 'web'}
b = {'name' => 201, 'segment' => 'shared', 'dc' => 'nyc', 'designation' => 'generic'}
c = {'name' => 202, 'segment' => 'alpha', 'dc' => 'nyc', 'designation' => 'app'}
d = {'name' => 400, 'segment' => 'alpha',  'dc' => 'sfc', 'designation' => 'web'}
e = {'name' => 401, 'segment' => 'shared', 'dc' => 'sfc', 'designation' => 'web'}
f = {'name' => 402, 'segment' => 'shared', 'dc' => 'sfc', 'designation' => 'app'}
g = {'name' => 403, 'segment' => 'alpha', 'dc' => 'sfc', 'designation' => 'app'}

members = [a,b,c,d,e,f,g]

puts '=== without group_by ==='

class Array # reopen class Array
    def groupedByKey(p_key)
        new_h = {}

        self.each do | el | # el must be a hash
            key = el[p_key]
            unless new_h[key]
            then # the key does not exist, create a key - value pair
                new_h[key] = [el] # the value is an array with the whole element
            else # a key and value already exist ...
                new_h[key] << el # ... push the new value onto the array
            end
        end

        new_h
    end
end # class Array

h1 = members.groupedByKey('segment')
p h1
h1.each { | k1, v1 | h1[k1] = v1.groupedByKey('dc') }
puts
p h1
h1.each { | k1, v1 | v1.each { | k2, v2 | v1[k2] = v2.groupedByKey('designation') } }
puts
p h1
h1.each { | k1, v1 | v1.each { | k2, v2 | v2.each { | k3, v3 | v2[k3] = v3.collect { | el | el['name'] } } } }
puts '--- final result ---'
p h1

执行(在2.0中):

$ ruby -w t2.rb 
=== without group_by ===
{"alpha"=>[{"name"=>200, "segment"=>"alpha", "dc"=>"nye",
...
--- final result ---
{"alpha"=>{"nyc"=>{"web"=>[200], "app"=>[202]}, "sfc"=>{"web"=>[400], "app"=>[403]}},
"shared"=>{"nyc"=>{"generic"=>[201]}, "sfc"=>{"web"=>[401], "app"=>`[402]}}}

修改

结合Cary的Hash#transform_values

a = {'name' => 200, 'segment' => 'alpha',  'dc' => 'nyc', 'designation' => 'web'}
b = {'name' => 201, 'segment' => 'shared', 'dc' => 'nyc', 'designation' => 'generic'}
c = {'name' => 202, 'segment' => 'alpha', 'dc' => 'nyc', 'designation' => 'app'}
d = {'name' => 400, 'segment' => 'alpha',  'dc' => 'sfc', 'designation' => 'web'}
e = {'name' => 401, 'segment' => 'shared', 'dc' => 'sfc', 'designation' => 'web'}
f = {'name' => 402, 'segment' => 'shared', 'dc' => 'sfc', 'designation' => 'app'}
g = {'name' => 403, 'segment' => 'alpha', 'dc' => 'sfc', 'designation' => 'app'}

members = [a,b,c,d,e,f,g]

puts '=== should work in Ruby 1.8 ==='

class Array # reopen class Array
    def groupedByKey(p_key)
        new_h = {}

        self.each do | el | # el must be a hash
            key = el[p_key]
            unless new_h[key]
            then # the key does not exist, create a key - value pair
                new_h[key] = [el] # the value is an array with the whole element
            else # a key and value already exist ...
                new_h[key] << el # ... push the new value onto the array
            end
        end

        new_h
    end
end # class Array

class Hash
    def transformValues
        Hash[self.collect { | k, v | [ k, yield(v) ] } ]
    end
end

# Step by step
h1 = members.groupedByKey('segment')
puts '--- grouped by segment'
p h1
h2 = h1.transformValues { | v1 | v1.groupedByKey('dc') }
puts '--- grouped by dc'
p h2
h2 = members.groupedByKey('segment').
     transformValues { | v1 | v1.groupedByKey('dc').
        transformValues { | val | val.groupedByKey('designation') } }
puts '--- grouped by designation'
p h2

# One step.
# Note that the hash returned by each groupedByKey is immediately transformed
# (watch the dot after groupedByKey('xxx')).
h2 = members.groupedByKey('segment').
     transformValues { | v1 | v1.groupedByKey('dc').
        transformValues { | v2 | v2.groupedByKey('designation').
            transformValues { | v3 | v3.collect { | el | el['name'] } } } }
puts '--- final result ---'
p h2