Techioz Blog

Ruby で特定の値によってマージしながら 2 つのオブジェクトの配列を加算する

概要

私は Ruby On Rails を使用していて、それぞれがオブジェクトの配列を返す 2 つの SQL クエリがあります。これら 2 つの SQL クエリは 2 つの別個の DB からのものであるため、異なるフィールドがありますが、結果を 1 つのリストにマージする必要があります。以下に 2 つの SQL クエリの例と、最終結果がどのようになるかを示します。

SQL1 result == [{id1: 1, name: john, role: user},{id1: 2, name: matt, role: admin}]
SQL2 result == [{id2: 4, externalName: john},{id2: 8, externalName: ronald}]

name == externalName のオブジェクトを 1 つのオブジェクトに結合し、残りはそのままにしておく必要があります。

RESULT == [{id1: 1, name: john, role: user, id2: 4},{id1: 2, name: matt, role: admin},{id2: 8, externalName: ronald}]

基本的に、両方のリストを一緒に追加したいのですが、2 番目のリストの :externalName が最初のリストの :name 値と等しい場合、またはその逆の場合、これら 2 つの別々のオブジェクトが考慮されるため、これら 2 つの別々のオブジェクトを 1 つに結合したいと考えています。 「重複」。 2 つのオブジェクトを組み合わせた結果は、{:id1, :name, :role, :id2} のようになります。これを満たさない残りのオブジェクトは、結果の配列内に表示されます。

両方の配列を素敵な配列に追加する concat を実行しましたが、name == externalName ではマージされません。 .merge() を使用することを考えましたが、オブジェクトの 2 つの配列全体でこれを行う方法と、マージ条件が満たされなかった場合に何が起こるか、結果のリストにそれらが引き続き表示されるかがわかりません。マップについても考えましたが、結果のオブジェクトを他のオブジェクトにマージした後にそれを削除する方法を理解する必要があります。現時点では何が最善のアプローチであるかわかりませんが、いくつかのヒントを探しています。

解決策

これは次のように実行できます。

sql1 = [{id1: 1, name: 'john', role: 'user'},{id1: 2, name: 'matt', role: 'admin'}]
sql2 = [{id2: 4, externalName: 'john'},{id2: 8, externalName: 'ronald'}]
[*sql1,*sql2]
  .group_by { |h| h[:name] || h[:externalName] }
  .flat_map do |k,v| 
     v.reduce(&:merge).tap { |h| h.delete(:externalName) if h.key?(:name) }
  end
#=> => [{:id1=>1, :name=>"john", :role=>"user", :id2=>4}, {:id1=>2, :name=>"matt", :role=>"admin"}, {:id1=>17, :role=>"user"}, {:id2=>8, :externalName=>"ronald"}]

手順:

アップデート どうやら name は必須キーではないため、マージリダクションがオプションではない、nil 値の大きなグループが生成されます。 nil グループ化を処理するようにコードを更新しました。

sql1 = [{id1: 1, name: 'john', role: 'user'},{id1: 2, name: 'matt', role: 'admin'}, {id1: 17, role: 'user'}, {id1: 12, role: 'admin'} ]
sql2 = [{id2: 4, externalName: 'john'},{id2: 8, externalName: 'ronald'}, {id2: 42}]
[*sql1,*sql2]
  .group_by { |h| h[:name] || h[:externalName] }
  .flat_map do |k,v| 
     next v unless k
     v.reduce(&:merge).tap { |h| h.delete(:externalName) if h.key?(:name) }
  end
#=> [{:id1=>1, :name=>"john", :role=>"user", :id2=>4}, {:id1=>2, :name=>"matt", :role=>"admin"}, {:id1=>17, :role=>"user"}, {:id1=>12, :role=>"admin"}, {:id2=>42}, {:id2=>8, :externalName=>"ronald"}]

その他の仮定

これらは投稿に基づいた仮定です。