0

I have following three array of hashes.

customer_mapping = [
      {:customer_id=>"a", :customer_order_id=>"g1"},
      {:customer_id=>"b", :customer_order_id=>"g2"},
      {:customer_id=>"c", :customer_order_id=>"g3"},
      {:customer_id=>"d", :customer_order_id=>"g4"},
      {:customer_id=>"e", :customer_order_id=>"g5"}
    ]

customer_with_products = [
      {:customer_order_id=>"g1", :product_order_id=>"a1"},
      {:customer_order_id=>"g2", :product_order_id=>"a2"},
      {:customer_order_id=>"g3", :product_order_id=>"a3"},
      {:customer_order_id=>"g4", :product_order_id=>"a4"},
      {:customer_order_id=>"g5", :product_order_id=>"a5"}
    ]

product_mapping = [
    {:product_id=>"j", :product_order_id=>"a1"},
    {:product_id=>"k", :product_order_id=>"a2"},
    {:product_id=>"l", :product_order_id=>"a3"}
  ]

What i want is a new hash with only customer_id and product_id

{:product_id=>"j", :customer_id=>"a"},
{:product_id=>"k", :customer_id=>"b"},
{:product_id=>"l", :customer_id=>"c"}

I tried to loop over product_mapping and select the customer_order_id that match product_order_id in customer_with_products and then thought of looping over customer_mapping but not able to get desired output from the first step.

How can i achieve this?

5 Answers 5

1

Using

def merge_by(a,b, key)
  (a+b).group_by { |h| h[key] }
       .each_value.map { |arr| arr.inject(:merge) }
end

merge_by(
  merge_by(customer_mapping, customer_with_products, :customer_order_id),
  product_mapping,
  :product_order_id
).select { |h| h[:product_id] }.map { |h| h.slice(:product_id, :customer_id) }

#=>[{:product_id=>"j", :customer_id=>"a"},
#   {:product_id=>"k", :customer_id=>"b"},
#   {:product_id=>"l", :customer_id=>"c"}]

Definitely not the cleanest solution, if your initial arrays come from SQL queries, I think those queries could be modified to aggregate your data properly.

merge_by(customer_mapping, customer_with_products, :customer_order_id)
# => [{:customer_id=>"a", :customer_order_id=>"g1", :product_order_id=>"a1"},
#     {:customer_id=>"b", :customer_order_id=>"g2", :product_order_id=>"a2"},
#     {:customer_id=>"c", :customer_order_id=>"g3", :product_order_id=>"a3"},
#     {:customer_id=>"d", :customer_order_id=>"g4", :product_order_id=>"a4"},
#     {:customer_id=>"e", :customer_order_id=>"g5", :product_order_id=>"a5"}]

Then merge it similarly with your last array and cleanup the result selecting only the elements for which :product_id was found, slicing wanted keys.

Alternatively, a much more readable solution, depending on your array sizes might be slower as it keeps iterating over the hashes:

product_mapping.map do |hc| 
  b_match = customer_with_products.detect { |hb| hb[:product_order_id] == hc[:product_order_id] }
  a_match = customer_mapping.detect { |ha| ha[:customer_order_id] == b_match[:customer_order_id] }
  [hc, a_match, b_match].inject(:merge)
end.map { |h| h.slice(:product_id, :customer_id) }
Sign up to request clarification or add additional context in comments.

Comments

1

Following your handling of the problem the solution would be the following:

result_hash_array = product_mapping.map do |product_mapping_entry|
        customer_receipt = customer_with_products.find do |customer_with_products_entry|
            product_mapping_entry[:product_order_id] == customer_with_products_entry[:product_order_id]
        end
        customer_id = customer_mapping.find do |customer_mapping_entry|
            customer_receipt[:customer_order_id] == customer_mapping_entry[:customer_order_id]
        end[:customer_id]
        {product_id: product_mapping_entry[:product_id], customer_id: customer_id}
end

Output

results_hash_array => [{:product_id=>"j", :customer_id=>"a"},
                       {:product_id=>"k", :customer_id=>"b"},
                       {:product_id=>"l", :customer_id=>"c"}]

Comments

0

Other option, starting from customer_mapping, one liner (but quite wide):

customer_mapping.map { |e| {customer_id: e[:customer_id], product_id: (product_mapping.detect { |k| k[:product_order_id] == (customer_with_products.detect{ |h| h[:customer_order_id] == e[:customer_order_id] } || {} )[:product_order_id] } || {} )[:product_id]  } }

#=> [{:customer_id=>"a", :product_id=>"j"},
#    {:customer_id=>"b", :product_id=>"k"},
#    {:customer_id=>"c", :product_id=>"l"},
#    {:customer_id=>"d", :product_id=>nil},
#    {:customer_id=>"e", :product_id=>nil}]

Comments

0
cust_order_id_to_cust_id =
  customer_mapping.each_with_object({}) do |g,h|
    h[g[:customer_order_id]] = g[:customer_id]
  end
  #=> {"g1"=>"a", "g2"=>"b", "g3"=>"c", "g4"=>"d", "g5"=>"e"}

prod_order_id_to_cust_order_id =
  customer_with_products.each_with_object({}) do |g,h|
    h[g[:product_order_id]] = g[:customer_order_id]
  end
  #=> {"a1"=>"g1", "a2"=>"g2", "a3"=>"g3", "a4"=>"g4", "a5"=>"g5"}

product_mapping.map do |h|
  { product_id: h[:product_id], customer_id: 
    cust_order_id_to_cust_id[prod_order_id_to_cust_order_id[h[:product_order_id]]] }
end
  #=> [{:product_id=>"j", :customer_id=>"a"},
  #    {:product_id=>"k", :customer_id=>"b"},
  #    {:product_id=>"l", :customer_id=>"c"}]

This formulation is particularly easy to test. (It's so straightforward that no debugging was needed).

Comments

0

I would recommended to rather take a longer but more readable solution which you also understand in some months from now by looking at it. Use full names for the hash keys instead of hiding them behind k, v for more complexe lookups (maybe its just my personal preference).

I would suggest somethink like:

result = product_mapping.map do |mapping|
  customer_id = customer_mapping.find do |hash|
    hash[:customer_order_id] == customer_with_products.find do |hash|
      hash[:product_order_id] == mapping[:product_order_id]
    end[:customer_order_id]
  end[:customer_id]

  { product_id: mapping[:product_id], customer_id: customer_id }
end

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.