1

I have an array containing hashes, like this:

[
    {:id => 1, :week => 1, :year => 2014},
    {:id => 2, :week => 2, :year => 2014},
    {:id => 1, :week => 1, :year => 2015},
    {:id => 2, :week => 5, :year => 2015},
]

What i need is a array of arrays, containing each two values: The first value is a hash from the first array with year 2014, the second is a hash from the first array with year 2015, if it has the same week and id as the first hash.

If there is not hash with equal id and week from 2015, then the second value has to be nil, vice versa the first value is nil.

For the array above, the new array should look like:

[
    [{:id => 1, :week => 1, :year => 2014}, {:id => 1, :week => 1, :year => 2015}],
    [{:id => 2, :week => 2, :year => 2014}, nil],
    [nil, {:id => 2, :week => 5, :year => 2015}],
]

-e- my approach:

result = []
all_ids.each do |id|
  all_weeks.each do |week|
    v1 = array.select{ |v| v.id == id && v.week == week && v.year == 2014}        
    v2 = array.select{ |v| v.id == id && v.week == week && v.year == 2015}
    v1 = v1.length == 1 ? v1.first : nil
    v2 = v2.length == 1 ? v1.first : nil
    result << [v1, v2]
  end

end

this doesn't seem to be very efficient as i have to iterate the array multiple times

1
  • 1
    a.group_by { |el| [el[:id], el[:week]] } might be a good way to go. Commented Feb 5, 2015 at 13:49

2 Answers 2

1

Use group_by to separate your hashes by week and id, then you just need to pad out the arrays that don't have two values:

array.group_by { |v| [v[:id], v[:week]] }.values.each do |a|
  a.insert(2015 - a[0][:year], nil) if a.length == 1
end
Sign up to request clarification or add additional context in comments.

Comments

0

Here is a way that is intended to emphasize readability (seriously).

Code

    def convert(arr)
      a14 = arr.select { |h| h[:year] == 2014 }
      a15 = arr - a14
      arr = a14.map do |h14|
        i15 = a15.index { |h15| h14[:id]==h15[:id] && h14[:week]==h15[:week] }
        [ h14, i15.nil? ? nil : a15.delete_at(i15) ]
      end
      arr.concat([nil].product(a15)) if a15.any?
    end

If the value of :year can be other than 2014 or 2015, replace a15 = arr - a14 with:

a15 = arr.select { |h| h[:year] == 2015 }

Note that a15.delete_at(i15) serves a dual-function: it removes the value at index i15 from a15 and returns that value for inclusion in the tuple.

Example

arr = [ {:id => 1, :week => 1, :year => 2014},
        {:id => 2, :week => 2, :year => 2014},
        {:id => 1, :week => 1, :year => 2015},
        {:id => 2, :week => 5, :year => 2015} ]

convert(arr)
  #=> [[{:id=>1, :week=>1, :year=>2014}, {:id=>1, :week=>1, :year=>2015}],
  #    [{:id=>2, :week=>2, :year=>2014}, nil],
  #    [nil, {:id=>2, :week=>5, :year=>2015}]] 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.