Reputation: 27862
I have two arrays of hashes that look like this:
a = [ { car_id: 1, motor_id: 1, quantity: 5 },
{ car_id: 1, motor_id: 2, quantity: 6 },
{ car_id: 5, motor_id: 3, quantity: 3 } ]
b = [ { car_id: 1, motor_id: 1, quantity: 2 },
{ car_id: 1, motor_id: 2, quantity: 3 } ]
I want to substract the quantities from each hash in b
from the hashes in a
for those hashes that have the same car_id
& motor_id
. So, my expected result would be:
c = [ {car_id: 1, motor_id: 1, quantity: 3},
{car_id: 1, motor_id: 2, quantity: 3},
{car_id: 5, motor_id: 3, quantity: 3 } ]
What is a good way to do this in Ruby? My thoughts were to iterate over a
, and for each element find if there is any in b
that have the same car_id
and motor_id
, if so, substract, and continue.
Upvotes: 0
Views: 86
Reputation: 158
Perhaps, you'd find the following more intuitive:
matched_result = []
while current_a = a.shift
if current_b = b.shift
if current_a[:car_id] == current_b[:car_id] && current_a[:motor_id] == current_b[:motor_id]
matched_result << {car_id: current_a[:car_id], motor_id: current_a[:motor_id], quantity: current_a[:quantity] - current_b[:quantity]}
end
else
matched_result << current_a
end
end
p matched_result
Upvotes: 0
Reputation: 110755
I suggest you first create a hash bqty
for b
that, for each element (hash) g
of b
, maps [g[:car_id], g[:motor_id]]
into g[:quantity]
:
bqty = b.each_with_object({}) {|g,h| h[[g[:car_id], g[:motor_id]]] = g[:quantity]}
#=> {[1, 1]=>2, [1, 2]=>3}
Next, map each element (hash) g
of a
to the desired hash. This is done by merging g
into an empty hash h
(or g.dup
), then, if there is an element of bqty
with the key key = [h[:car_id], h[:motor_id]]
, subtract bqty[key]
from h[:quantity]
. Note this leaves a
and b
unchanged.
a.map do |g|
{}.merge(g).tap do |h|
key = [h[:car_id], h[:motor_id]]
h[:quantity] -= bqty[key] if bqty.key?(key)
end
end
#=> [{:car_id=>1, :motor_id=>1, :quantity=>3},
# {:car_id=>1, :motor_id=>2, :quantity=>3},
# {:car_id=>5, :motor_id=>3, :quantity=>3}]
An alternative to the antepenultimate1 line is:
h[:quantity] -= bqty[key].to_i
since nil.to_i #=> 0
.
1. How can one pass up an opportunity to use such a word?
Upvotes: 2
Reputation: 7223
This is basically what you suggested, except instead of an explicit check for car_id
/motor_id
it uses a hash instead.
ids = ->(h){h.values_at(:car_id, :motor_id)} # A function for extracting the key values
lookup = Hash[*a.flat_map{|h| [ids[h], h]}] # A map from ids -> hash w/ quantity
b.each{|h| lookup[ids[h]][:quantity] -= h[:quantity]} # Subtract each b from an a (will error if the a doesn't exist)
a == c # == true
Upvotes: 0