Coda Chang Coda Chang - 3 months ago 15
Ruby Question

Rails combine two array with hash structure

I got two array which seems similar, part of them are duplicate, and I need to combine them.

Here is the first array.

first

[
{
:group_id => "12873",
:question_sets => [
{
:id => 29435,
:name => "Question1"
},
{
:id => 29349,
:name => "Question2"
},
]
},
{
:group_id => "12876",
:question_sets => [
{
:id => 29443,
:name => "Question3"
}
]
}
]


Here is second array

[
{
:group_id => "12873",
:question_sets => [
{
:id => 29435,
:name => "Question1"
},
{
:id => 29338,
:name => "Question4"
},
]
},
{
:group_id => "12888", #(not in first array)
:question_sets => [
{
:id => 29443,
:name => "Question3"
}
]
}
]


The idea was to combine the question_sets's
id
and
name
by the same
group_id
, every record in second array need to be combined. If there is no the same
group_id
, create the
group_id
.

The result will be like

[
{
:group_id => "12873",
:question_sets => [
{
:id => 29435,
:name => "Question1"
},
{
:id => 29349,
:name => "Question2"
},
{
:id => 29338,
:name => "Question4"
}
]
},
{
:group_id => "12876",
:question_sets => [
{
:id => 29443,
:name => "Question3"
}
]
},
{
:group_id => "12888",
:question_sets => [
{
:id => 29443,
:name => "Question3"
}
]
}
]

Answer

If h1 and h2 are hashes with keys :id and :name, I've assumed that h1[:name] == h2[:name] if and only if h1[:id] == h2[:id]

If a1 and a2 equal your two arrays, you could do the following.

(a1+a2).group_by { |h| h[:group_id] }.
        map { |k,v| { group_id: k,
                      question_sets: v.flat_map { |g| g[:question_sets] }.uniq } }
  #=> [{:group_id=>"12873",
  #     :question_sets=>[
  #       {:id=>29435, :name=>"Question1"},
  #       {:id=>29349, :name=>"Question2"},
  #       {:id=>29338, :name=>"Question4"}
  #      ]
  #    },
  #    {:group_id=>"12876",
  #     :question_sets=>[{:id=>29443, :name=>"Question3"}]
  #    },
  #    {:group_id=>"12888",
  #     :question_sets=>[{:id=>29443, :name=>"Question3"}]
  #    }
  #   ] 

The steps are as follows.

a = a1+a2
  #=> [{:group_id=>"12873",
  #     :question_sets=>[
  #       {:id=>29435, :name=>"Question1"},
  #       {:id=>29349, :name=>"Question2"}
  #     ]
  #    },
  #    {:group_id=>"12876",
  #     :question_sets=>[{:id=>29443, :name=>"Question3"}]
  #    },
  #    {:group_id=>"12873",
  #     :question_sets=>[
  #       {:id=>29435, :name=>"Question1"},
  #       {:id=>29338, :name=>"Question4"}
  #     ]
  #    },
  #    {:group_id=>"12888",
  #     :question_sets=>[{:id=>29443, :name=>"Question3"}]
  #    }
  #   ]

b = a.group_by { |h| h[:group_id] }
  #=> {"12873"=>[
  #     {:group_id=>"12873",
  #      :question_sets=>[
  #        {:id=>29435, :name=>"Question1"},
  #        {:id=>29349, :name=>"Question2"}
  #      ]
  #     },
  #     {:group_id=>"12873",
  #      :question_sets=>[
  #        {:id=>29435, :name=>"Question1"},
  #        {:id=>29338, :name=>"Question4"}
  #      ]
  #     }
  #    ],
  #    "12876"=>[
  #      {:group_id=>"12876",
  #       :question_sets=>[{:id=>29443, :name=>"Question3"}]
  #      }
  #    ],
  #    "12888"=>[
  #      {:group_id=>"12888",
  #       :question_sets=>[{:id=>29443, :name=>"Question3"}]
  #      }
  #    ]
  #   }

b.map { |k,v| { group_id: k,
                question_sets: v.flat_map { |g| g[:question_sets] }.uniq } }
  #=> array of hashes shown above.

Consider the first element of b passed to map's block, to which the block variables are assigned:

k,v = b.first   
k #=> "12873",
v #=> [{:group_id=>"12873",
  #     :question_sets=>[
  #       {:id=>29435, :name=>"Question1"},
  #       {:id=>29349, :name=>"Question2"}
  #     ]
  #    },
  #    {:group_id=>"12873",
  #     :question_sets=>[
  #       {:id=>29435, :name=>"Question1"},
  #       {:id=>29338, :name=>"Question4"}
  #     ] 
  #    }
  #   ]

so the block calculation, which constructs a hash, is as follows.

c = v.flat_map { |g| g[:question_sets] }
  #=> [{:id=>29435, :name=>"Question1"},
  #    {:id=>29349, :name=>"Question2"},
  #    {:id=>29435, :name=>"Question1"},
  #    {:id=>29338, :name=>"Question4"}]
d = c.uniq
  #=> [{:id=>29435, :name=>"Question1"},
  #    {:id=>29349, :name=>"Question2"},
  #    {:id=>29338, :name=>"Question4"}]

so the block returns the hash

{ group_id: k, question_sets: d }
  #=> { group_id: "12873",
  #     question_sets: [
  #       {:id=>29435, :name=>"Question1"},
  #       {:id=>29349, :name=>"Question2"},
  #       {:id=>29338, :name=>"Question4"}
  #     ]
  #   }

The remaining calculations are similar.