Help us understand the problem. What is going on with this article?

Javascript:オブジェクトをマージする関数mergeを考えてみた

連想配列(辞書オブジェクト)n個の共通配列を上書きなしで結合マージするを読んであれこれやってみたのが勉強になったので、あちらのコメントにも書いたが、あんまり書くと先方にも迷惑なのでこちらに書きます。

こうなったらいいな

//こんなオブジェクトがあったとして:
const hira = {
  'dog': 'いぬ',
  'cat': ['ねこ'],
  'human': 'ひと',
}
const kana = {
  'dog': ['イヌ'],
  'cat': ['ネコ', 'ニャンコ'],
}
const kan = {
  'dog': [],
  'cat': '',
  'human': '',
  'car' : '',
}

//こうなったらいいな...
merge(hira,kana,kan,{})
/*
{
  dog: [ 'いぬ', 'イヌ' ],
  cat: [ 'ねこ', 'ネコ', 'ニャンコ', '猫' ],
  human: [ 'ひと', '人' ],
  car: [ '車' ]
}
*/
  • 同じキーがあったら値を配列にまとめます。
  • 値は配列になってたり、なってなかったりします。

ってことです。(元記事とはちょっと変えてあります。)

こんな感じにしてみました

const merge = ( ...objs ) => objs.reduce( updateMerge, {} )

const updateMerge = (acc, obj) =>  reduceObj( updateObj )( acc )( obj )
const reduceObj = update => init => obj => Object.entries( obj ).reduce( update, init )
const updateObj = (acc, [k, v]) => 
  acc.hasOwnProperty(k) ? { ...acc, [k]:[ acc[k], v ].flat() }
  : { ...acc, [k]: [v].flat() }

こう考えた

おおざっぱに言って、merge はどんな関数でしょう?

  • オブジェクトを複数、引数にし、オブジェクトを返す
  • 引数のオブジェクトを いい感じで空のオブジェクトに足していけばいいのでは?

reduceのことを知っていれば、まさにreduce案件だと気がつきます。
とりあえず、こんな風に書いてみます。

const merge = ( ...objs ) => objs.reduce( updateMerge, {} )

updateMerge というのが出てきましたが、とりあえず置いただけです。中味はまだ決まってませんが「いい感じで足していく」ものです。
さて、updateMergeってどんな関数でしょう?

  • アキュムレータと配列の要素(ここではオブジェクト)を引数にとって、新しいアキュムレータを返す
  • そのオブジェクトの各プロパティを、いい感じでそのアキュムレータに足していけばいいのでは?

これも reduce 案件のようです。
しかしオブジェクトに直接 reduce は使えないので、とりあえずこうしておきます。

const updateMerge = (acc, obj) =>  reduceObj( updateObj )( acc )( obj )

新しい関数がまたふたつ出てきました。これもとりあえず置いただけです。

  • reduceObj はオブジェクトで reduce っぽいことをする関数です。
  • updateObj も「いい感じで足していく」ものです。

まず、reduceObj を考えます。
これは簡単です。Object.entries()を使ってオブジェクトを [(キー), (値)] の配列にして、reduce すればいい。

const reduceObj = update => init => obj => Object.entries( obj ).reduce( update, init )

つぎに、updateObj を考えます。

  • アキュムレータと配列の要素(ここでは [(キー), (値)] )を引数にとって、新しいアキュムレータを返す
  • アキュムレータに既にそのプロパティがあれば、値を値の配列の最後部に加える
  • まだそのプロパティがなければ、新しく作って追加する
  • 値は配列にはいってたりはいってなかったりするので、いい感じにする

こうしてみました。

const updateObj = (acc, [k, v]) => 
  acc.hasOwnProperty(k) ? { ...acc, [k]:[ acc[k], v ].flat() }
  : { ...acc, [k]: [v].flat() }

flat()を使うと配列の中のかっこを一個へらしていい感じでフラットにしてくれます。

これで、「とりあえず置いた」ものはなくなりました。
全部並べて、動くかな? -> 動いた! わーい!

まとめ

  • トップダウンで考えると見通しがいい場合がある
  • 関数内から別の関数を呼ぶときは、どっちが上でもよいらしい
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした