How to map ruby hashes correctly based on key provided

57 views Asked by At

My data is like:

h = { themes_data: {
    Marketing: [
        {
            id: 68,
            projectno: "15",
        }
    ],
    Produktentwicklung: [
        {
            id: 68,
            projectno: "15",
        },
        {
            id: 4,
            projectno: "3",
        }
    ],
    Marketing_summary: [
        {
            ges: {
                result: "47.6"
            },
            theme: "Marketing"
        }
    ],
    Produktentwicklung_summary: [
        {
            ges: {
                result: "87.7"
            },
            theme: "Produktentwicklung"
        }
    ]
  }
}

And my output should be like:

{ "marketing" => [
    {
      id: 68,
      projectno: "15",
    },
    {
      ges: { 
        result: "47.6"
      },
      theme: "Marketing"
    }
  ],
  "Produktentwicklung" => [
    {
      id: 68,
      projectno: "15"
    },
    {
      id: 4,
      projectno: "3",
    },
    {
      ges: {
        result: "87.7"
      },
      theme: "Produktentwicklung"
    }
  ]
}

Code:

def year_overview_theme
  branch_hash = {}
  @themes_data.each do |td|
    arr = []
    td[1].map do |dt|
      arr << [{content: dt[:projectno], size: 5, align: :right, background_color: 'D8E5FF'}]
    end
    branch_hash["#{td[0]}"] = arr
  end
  branch_hash
end

The problem is that it does not iterate for right hash key.

For example, i want like:

marketing + marketing_summary as 1 hash and similarly

Produktentwicklung = Produktentwicklung_summary as one hash but there is some problem in my logic.

Is there a way that I can check like after 2 iteration,

it should do arr << data with branch_hash["#{td[0]}"] = arr ?

3

There are 3 answers

1
Cary Swoveland On BEST ANSWER

The desired hash can be constructed as follows.

h[:themes_data].each_with_object({}) { |(k,v),g| 
  g.update(k.to_s[/[^_]+/]=>v) { |_,o,n| o+n } }
  #=> { "Marketing"=>[
  #       {:id=>68, :projectno=>"15"},
  #       {:ges=>{:result=>"47.6"}, :theme=>"Marketing"}
  #     ],
  #     "Produktentwicklung"=>[
  #       {:id=>68, :projectno=>"15"},
  #       {:id=>4, :projectno=>"3"},
  #       {:ges=>{:result=>"87.7"}, :theme=>"Produktentwicklung"}
  #     ]
  #   } 

This uses the form of Hash#update (aka merge) that employs a block to determine the values of keys that are present in both hashes being merged. Here that block is:

{ |_,o,n| o+n }

The first block variable, _, is the common key. I have represented it with an underscore (a valid local variable) to tell the reader that it is not used in the block calculation. That is common practice. The values of the other two block variables, o and n, are explained at the link for the method update.

The regular expression /[^_]+/, matches one or more characters from the start of the string that are not (^) underscores. When used with the method String#[], we obtain:

"Marketing"[/[^_]+/]         #=> "Marketing"
"Marketing_summary"[/[^_]+/] #=> "Marketing" 
0
Clemens Kofler On

Let me start with a note: This looks to me like something that should rather be solved in SQL (if it's coming from SQL) instead of Ruby.

With that out of the way, here's a solution that should work:

output = {}

themes_data.each do |theme, projects|
  projects.each do |project|
    key = project[:theme] || theme.to_s
    output[key] ||= [] # make sure the target is initialized
    output[key] << project
  end
end

There would probably be more elegant solutions using reduce or each_with_object but this works and it's simple enough.

0
Christian Bruckmayer On
keys = themes_data.keys
summary_keys = themes_data.keys.grep(/_summary/)

result = {}.tap do |hash|
  (keys - summary_keys).each do |key|
    hash[key] = themes_data[key] + themes_data["#{key}_summary".to_sym]
  end
end