0

My question

Here, I have 2 arrays and 1 hash,

@key_array = %w(year entry amount)

@nums = {
  "sales": [1000, 2000, 3000, 4000],
  "net_income": [20, 30, 50, 60],
}

@years = [2011, 2012, 2013, 2014]

What I would like to do is to turn those 3 into like this

[{"year"=>2011, "entry"=>:sales, "amount"=>1000},
 {"year"=>2012, "entry"=>:sales, "amount"=>2000},
 {"year"=>2013, "entry"=>:sales, "amount"=>3000},
 {"year"=>2014, "entry"=>:sales, "amount"=>4000},
 {"year"=>2011, "entry"=>:net_income, "amount"=>20},
 {"year"=>2012, "entry"=>:net_income, "amount"=>30},
 {"year"=>2013, "entry"=>:net_income, "amount"=>50},
 {"year"=>2014, "entry"=>:net_income, "amount"=>60}]

So far, I have tried this Ruby code...

def build_entry_nums_hash
  num_arr = []
  @years.each do |year|
    @nums.each do |key, value|
      value.each do |fin|
        value_array = [year, key, fin]
        hash = {}
        @key_array.zip(value_array).each { |k, v| hash[k] = v }
        num_arr << hash
      end
    end
  end
  num_arr
end

It returns almost what I expected, but a bit wrong. It returns some unnecessary hashes.

 [{"year"=>2011, "entry"=>:sales, "amount"=>1000},
 {"year"=>2011, "entry"=>:sales, "amount"=>2000},
 {"year"=>2011, "entry"=>:sales, "amount"=>3000},
 {"year"=>2011, "entry"=>:sales, "amount"=>4000},
 ......
 {"year"=>2014, "entry"=>:net_income, "amount"=>20},
 {"year"=>2014, "entry"=>:net_income, "amount"=>30},
 {"year"=>2014, "entry"=>:net_income, "amount"=>50},
 {"year"=>2014, "entry"=>:net_income, "amount"=>60}]

Would you give me some advice?

Current situation

I am not sure this is necessary, so please read through if you concerned why I have to convert those arrays and hash into one hash.

I am trying to parse some financial data from an website by using Nokogiri. The data is written in HTML Table tag and it looks like an Excel spreadsheet. My goal is to display those data by using my rails. Currently, I was able to get the numbers and struggling how could I put them into rails DB. It's good if I could do like..

Findata.create{"year"=>2011, "entry"=>:sales, "amount"=>1000} 

Sine it was very easy to get the financial numbers in row, I made a hahs like @num. What I need is to mix up other 2 arrays and get a hash what I need.

Edit: I have corrected mistakes in array with hash

2 Answers2

0
arr = []
@nums.each do |num_key, num_arr|
  @years.each_with_index do |year,i|
    arr << {"year" => year, "entry" => num_key.to_sym, "amount" => num_arr[i]}
  end
end;arr

Gives me

[{"amount"=>20, "entry"=>:net_income, "year"=>2011},
{"amount"=>30, "entry"=>:net_income, "year"=>2012},
{"amount"=>50, "entry"=>:net_income, "year"=>2013},
{"amount"=>60, "entry"=>:net_income, "year"=>2014},
{"amount"=>1000, "entry"=>:sales, "year"=>2011},
{"amount"=>2000, "entry"=>:sales, "year"=>2012},
{"amount"=>3000, "entry"=>:sales, "year"=>2013},
{"amount"=>4000, "entry"=>:sales, "year"=>2014}]

EDIT: put ";arr" after the loop so you're left with the arr variable which holds the hashes.

Max Williams
  • 32,435
  • 31
  • 130
  • 197
  • Ummm, _technically_ the code above will return you `@nums` :) Better to use `each_with_object` in the outer loop, I think. – Sergio Tulentsev Aug 19 '15 at 14:29
  • It doesn't matter what the loop returns: the required data is held in the variable `arr`. Apologies if this is not clear. – Max Williams Aug 19 '15 at 15:10
  • My point was, you don't _need_ extra complexity in form of local variable `arr`. Your updated code will return the array. It is also what `each_with_object` does internally, so might as well just use it (code will be cleaner) – Sergio Tulentsev Aug 19 '15 at 17:39
  • Sergio, if I remove arr from Max's code, it's just returns only the first element of what I want to get. – Takaaki Furuse Aug 19 '15 at 22:39
0

I refactored Max's answer.

  def build_hash
    @nums.flat_map do |num_key, num_arr|
      @years.map.with_index do |year, i|
        {"year" => year, "entry" => num_key.to_sym, "amount" => num_arr[i]}
      end
    end
  end

FYI, here is code and spec.

require 'spec_helper'

describe 'Array to Hash' do
  before do
    @key_array = %w(year entry amount)

    @nums = {
        "sales": [1000, 2000, 3000, 4000],
        "net_income": [20, 30, 50, 60],
    }

    @years = [2011, 2012, 2013, 2014]
  end

  let(:expected) do
    [{"year"=>2011, "entry"=>:sales, "amount"=>1000},
     {"year"=>2012, "entry"=>:sales, "amount"=>2000},
     {"year"=>2013, "entry"=>:sales, "amount"=>3000},
     {"year"=>2014, "entry"=>:sales, "amount"=>4000},
     {"year"=>2011, "entry"=>:net_income, "amount"=>20},
     {"year"=>2012, "entry"=>:net_income, "amount"=>30},
     {"year"=>2013, "entry"=>:net_income, "amount"=>50},
     {"year"=>2014, "entry"=>:net_income, "amount"=>60}]
  end

  def build_hash
    @nums.flat_map do |num_key, num_arr|
      @years.map.with_index do |year, i|
        {"year" => year, "entry" => num_key.to_sym, "amount" => num_arr[i]}
      end
    end
  end

  specify do
    expect(build_hash).to eq expected
  end
end
Junichi Ito
  • 2,438
  • 1
  • 23
  • 46