如何递归这个哈希数组

时间:2016-05-25 13:28:05

标签: arrays ruby recursion hash

我想知道如何用递归对这个散列数组中的“解析”值求和。

输入:

   [{"id"=>"1234",
     "id_data"=>
      [{"segment"=>{"segment_name"=>"Android"},
        "metrics"=>
          {
            "logins"=>[1000, 2000],
            "sign_ups_conversion"=>{
               "count"=>[500, 200],
               "cost"=>[2, 4]
            }
          },
       },
       {"segment"=>{"segment_name"=>"iOS"},
        "metrics"=>
          {
            "logins"=>[5000, 10000],
            "sign_ups_conversion"=>{
               "count"=>[100, 50],
               "cost"=>[6, 8]
            }
          },
       }
      ]
    },
    {"id"=>"5678",
     "id_data"=>
      [{"segment"=>{"segment_name"=>"Android"},
        "metrics"=>
          {
            "logins"=>[3000, 2000],
            "sign_ups_conversion"=>{
               "count"=>[300, 400],
               "cost"=>[2, 4]
            }
          },
       },
       {"segment"=>{"segment_name"=>"iOS"},
        "metrics"=>
          {
            "logins"=>[5000, 10000],
            "sign_ups_conversion"=>{
               "count"=>[100, 50],
               "cost"=>[6, 8]
            }
          },
       }
      ]
    }]

输出

    {
      "Android"=>{
        "ids" => ['1234','5678'],
         "segment" => {"segment_name"=>"Android"},
         "id_data" => [{
            "logins" => [4000, 4000], # sum by index from 'Android' logins ("logins"=>[1000, 2000] & "logins"=>[3000, 2000]),
            "sign_ups_conversion" => {
              "count" => [800, 600], # sum by index from 'Android' sign ups count ("count"=>[500, 200] & "count"=>[300, 400])
              "cost" => [4, 8] # sum by index from 'Android' sign ups cost ("cost"=>[2, 4] & "cost"=>[2, 4])
            }  
         }]
      },
      "iOS"=>{
        "ids" => ['1234','5678'],
         "segment" => {"segment_name"=>"iOS"},
         "id_data" => [{
            "logins" => [10000, 20000], # sum by index from 'iOS' logins ("logins"=>[5000, 10000] & "logins"=>[5000, 10000]),
            "sign_ups_conversion" => {
              "count" => [200, 100], # sum by index from 'iOS' sign ups count ("count"=>[100, 50] & "count"=>[100, 50])
              "cost" => [12, 16] # sum by index from 'iOS' sign ups cost ("cost"=>[6, 8] & "cost"=>[6, 8])
            }  
         }]
      }
    }

我,试图用这种方法解决它,但它没有用哈希格式(sign_ups_conversion)计算分析,并且仍在计算结果应该如何等于输出。

    def aggregate_by_segments(stats_array)
      results = {}

      stats_array.each do |stats|
        stats['id_data'].each do |data|
          segment_name = data['segment']['segment_name']
          results[segment_name] ||= {}
          (results[segment_name]['ids'] ||= []) << stats['id']
          results[segment_name]['segment'] ||= data['segment']
          results[segment_name]['id_data'] ||= [{}]
          data['metrics'].each do |metric, values|
            next if skip_metric?(values)
            (results[segment_name]['id_data'][0][metric] ||= []) << values
          end
        end
      end
      sum_segments(results)
    end

    def sum_segments(segments)
      segments.each do |segment, segment_details|
        segment_details['id_data'][0].each do |metric, values|
          segment_details['id_data'][0][metric] = sum_segment_metric(values)
        end
      end
      segments
    end

    def sum_segment_metric(metric_value)
      metric_value.transpose.map { |x| x.reduce(:+) }
    end

    # I skipped hash format for now
    def skip_metric?(metric_values)
      !metric_values.is_a? Array
    end

    ############################################
    # calls it with aggregate_by_segments(input)
    ############################################

我相信我们应该使用递归,但我还在想,任何人都可以帮助我吗?

提前致谢!

2 个答案:

答案 0 :(得分:0)

这里的问题是如何访问这些数据结构,一个ruby策略可以使用每个数据迭代数组,并使用这样的连接哈希来连接键:

假设你的结构被保留:

<强>数组[散列[阵列[散列]]

array_hash.each do |stats|
  stats["id_data"].each do |h|
    puts h["metrics"]["sign_ups_conversion"]
  end
end
# => {"count"=>[500, 200], "cost"=>[2, 4]}
# => {"count"=>[100, 50], "cost"=>[6, 8]}
# => {"count"=>[300, 400], "cost"=>[2, 4]}
# => {"count"=>[100, 50], "cost"=>[6, 8]}

答案 1 :(得分:0)

我解决了。

    def aggregate_by_segments(stats_array)
      results = {}

      stats_array.each do |stats|
        stats['id_data'].each do |data|
          segment_name = data['segment']['segment_name']
          results[segment_name] ||= {}
          (results[segment_name]['ids'] ||= []) << stats['id']
          results[segment_name]['segment'] ||= data['segment']
          results[segment_name]['id_data'] ||= [{}]
          data['metrics'].each do |metric, values|
            hash_values(results[segment_name]['id_data'][0], metric, values) if values.is_a? Hash
            next if skip_metric?(values)
            (results[segment_name]['id_data'][0][metric] ||= []) << values
          end
        end
      end
      sum_segments(results)
    end

    def hash_values(metrics, metric, hash_values)
      hash_values.each do |k, v|
        next if skip_metric?(v)
        metrics[metric] ||= {}
        (metrics[metric][k] ||= []) << v
      end
    end

    def sum_segments(segments)
      segments.each do |segment, segment_details|
        segment_details['id_data'][0].each do |metric, values|
          segment_details['id_data'][0][metric] = sum_segment_metric(values)
        end
      end
      segments
    end

    def sum_segment_metric(metric_value)
      result = metric_value.transpose.map { |x| x.reduce(:+) } if metric_value.is_a? Array
      result = metric_value.each do |k, v|
        metric_value[k] = sum_segment_metric(v)
      end if metric_value.is_a? Hash
      result
    end

    def skip_metric?(metric_values)
      !metric_values.is_a? Array
    end

我知道代码非常难看。我稍后会重构它:)

感谢各位访问和评论建设性反馈。