質問: v)) ``` ### 試した結果 ``` { "description": "製品Bになりました。", "images": [ "5.jpg", ], "metadata": { "product_id": "6735", "options": [ "size", ] }, "name": "製品B", "active": true, "updated": 1528145599, } ``` ### 補足情報(FW/ツールのバージョンなど) Node.js
回答: こんにちは。 オブジェクトや配列に対してちょっと込み入った操作をするときに Lodash を利用するという選択自体は、間違いではありません。実際、私も同じ課題に直面したら、まずは、Lodash に便利なメソッドはないか?と探すと思います。 ただし、ご質問に挙げられている以下の _.omitBy を使ったコード ``` _.omitBy(objectB, (v, k) => objectA[k] === v); ``` だと、objectB から更新されたプロパティだけを残して他を除去(omit)したオブジェクトを得るという、期待した動作を満たせないのでは?という懸念があります。 たとえば以下の例 ``` const objectA = { id: 1, description: "製品Aです。", images: [ "1.jpg", "2.jpg", "3.jpg", ], }; const objectB = { id: 1, description: "製品Aです。", images: [ "1.jpg", "2.jpg", "3.jpg", ], }; console.log(_.omitBy(objectB, (v, k) => objectA[k] === v)); ``` では、 `objectA` と `objectB` とは内容が同じなので、更新のあったプロパティを持つオブジェクトとしては空オブジェクト `{}` が返ってきて欲しいところですが、上記を実行すると `images` が等しくないプロパティとして残ることになり、結果として`_.omitBy`の返すオブジェクトは、以下 ``` { images: [ "1.jpg", "2.jpg", "3.jpg", ] } ``` であることが、console.logの出力で確認できます。 これの検証用のサンプルを以下に上げましたので、ご確認ください。 * **内容が同じオブジェクトでもうまくいかない例:** 上記を勘案しまして、オブジェクトの差分を得るのにLodashを使ってみたこと、および `_.omitBy(objectB, (v, k) => objectA[k] === v)` というコードを拝読して、お伝えしたいこととしては、以下の3点です。 1. Lodashを使った修正案の見つけ方 2. Lodashを使わずに最小限の要求を満たすものを書いてみる 3. 基礎を見直すことと応用的なものを使うことのバランスについて これらについて以下順を追って回答します。 ### 1. Lodashを使った修正案の見つけ方 ご質問に > > 調べながらなんとかLodashというライブラリを使って > > > とありました。 また、 > > 周りに相談できる人もいない > > > ともありました。そのような状況の中で Lodashを見つけ出して、それを使ってみようと思い、実際コードを書いてみたというチャレンジは**とてもよい**と思います。ですので、まずは Lodashを使って、主題の「ネストされたオブジェクト間の差分を取得する方法」を探ってみましょう。 ご質問から業務でコードを書いている中での課題と思われますが、仕事の中でこの種の問題に遭遇した場合、すでに(優れたプログラマーである)誰かが作ってくれた、より確からしい実装に早くたどりつく必要がありますね。そういうときに、あくまで私の場合は、という限られた話にはなりますが、まずは適切な**(日本語を含まない)英文で**ググってからの stackoverflow または、使うモジュールの発信元であるGitHubレポジトリのissueに拠り所を求めることが多いです。 このご質問の解決策を見つけるときに、Lodashを使うというのは筋として悪くないので、 (1) まずは `"lodash difference between two objects"` で[グーグル検索](https://www.google.co.jp/search?q=lodash+ difference +between+two+objects) し、 (2) 検索結果で上位に出てきたものをざっと読み、 (3) stackoverflowの投稿が出てきたときは、いくつかの回答の中でどれが使えそうか当たりをつけます。 (4) 今回のご質問の場合、Lo-Dash Essentials という著書も書いている、 Adam Boduch さんによる回答: * を拝借するのがよいと思えました。 (ただし検索結果として出てきた、どの投稿あるいは回答が一番使えるか?を選ぶのは、個人の判断に委ねられるところなので、別の投稿や回答のほうが使えると判断される方もいらっしゃることでしょう。) 上記を経て作成したものが以下です。 ``` // 更新前のオブジェクト const objectA = { id: 1, description: "製品Aです。", images: [ "1.jpg", "2.jpg", "3.jpg", ], metadata: { product_id: 6735, options: [ "color", "size", ] }, name: "製品A", active: false, created: 1511420673, updated: 1528145536, }; // 更新後のオブジェクト const objectB = { id: 1, description: "製品Bになりました。", images: [ "5.jpg", ], metadata: { product_id: 6735, options: [ "size", ] }, name: "製品B", active: true, created: 1511420673, updated: 1528145599, }; // 更新されたプロパティの配列を得る。(https://stackoverflow.com/a/31686152) const diffProps = _.reduce(objectA, function(result, value, key) { return _.isEqual(value, objectB[key]) ? result : result.concat(key); }, []); console.log(diffProps); // 値が更新されたプロパティと、それらのobjectBでの値を持つオブジェクトを作成 const diffObj = diffProps.reduce((obj, prop) => { obj[prop] = objectB[prop]; return obj; }, {}); console.log(diffObj); ``` 上記のコードを以下に上げましたのでお試しください。 * この差分抽出コードだと、objectAとobjectBの内容が同じときは、差分のオブジェクトは以下 * のように、期待どおり空オブジェクトになります。 先ほど、 「業務上でこの種の問題に遭遇した場合、すでに(優れたプログラマーである)誰かが作ってくれた、より確からしい実装に早くたどりつく必要がありますね。」 と書きました。その観点でいうと、(1)から(4)のステップで大事なのは、 まずは(1)でいかに適切な英語で検索できるかだと思ってます。ですので、なるべく、stackoverflow のタイトルや、使いたいモジュールの発信元である GitHubレポジトリの issueタイトルにヒットしそうな検索ワードをあれこれと試します。 ### 2. Lodashを使わずに最小限の要求を満たすものを書いてみる 二点目としまして、あらためてterataillerさんのご質問を再読して(老婆心ながら)思うところとしては、ご質問にあるコード ``` _.omitBy(objectB, (v, k) => objectA[k] === v) ``` によって、差分のあるプロパティだけを取り出せると考えたのでしたら、 厳密な比較演算子`===` についての理解を点検する必要があるのでは? ということです。ですので、 > > 中身がブラックボックスなので、なんか良くわからないけど出来たという感覚 > > > のモヤモヤの原因の一端は、`===` が true になる条件の理解不足にあるかもしれません。モヤモヤにも良いモヤモヤと悪いモヤモヤとがあって、 `===`の理解不足によるモヤモヤは悪いモヤモヤなので、すぐに解消すべきと思います。 言い換えると、 * Lodash (なり、他の便利な何か)を採用することにし、それを使うために必要な情報に効率よくリーチして、ときには他の優れたプログラマーの成果物を土台として使ったりもしながら、どんなオブジェクトにも使える「オブジェクト一般の差分取得」というような、(いわば)大きなテーマのコードを持ってきて、締め切りに追われがちな限られた開発時間の中で、必要あれば修正も加えて、自分の目の前にある課題解決に使うことができる。 というのは応用編のスキル、しかもプログラミングの技術力は半分であとの半分は仕事をさばく器用さに近いものですが、それよりも * `===` で true になる条件という基礎知識を正しく活用して、自分の開発業務の範囲内にある、ある特定の形式のオブジェクトを比較するコードを、Lodashのような便利グッズを使わず、スクラッチから難なく書ける。 というスキルのほうが、獲得すべき順序として優先度が(かなり)高い ということです。 そこで、以下に挙げたいくつかのページ * MDN:比較演算子 * 開眼!JavaScript 1.14節「オブジェクトは同値判定に参照を使用」に掲載のサンプルコード * JavaScript 忘れがちな === と == の違い (PianoScoreJPさん@Qiita) で説明している基礎知識を確認するための実践として、オブジェクト一般について比較するコードではなく、ご質問で検討の対象としている、以下の形式 ``` { id: 整数, description: 文字列, images: 文字列を要素とする配列, metadata: { product_id: 整数, options: 文字列を要素とする配列 }, name: 文字列, active: ブール値, created: 整数(タイムスタンプ), updated: 整数(タイムスタンプ), } ``` であることは分かっている2つのオブジェクト objectA と objectB とを比較して差分のプロパティだけを持つオブジェクトを返す関数なりメソッドなりを書いてみるのはいかがでしょうか? これをスクラッチから、Lodashのような便利なものを使わずに書いてみるとよいかもしれません。それがterataillerさんにとっての(次の一歩となる、) > > ベターな > > > コードになると思いますし、それが書けてからLodashを使った汎用的なコードを採用することにしても遅くはありません。 また、ご質問にある、 > > 中身がブラックボックスなので、なんか良くわからないけど出来たという感覚 > > > と書かれている、誰かが作った中身の分からないものに乗ってしまっていることでのモヤモヤ(これは良いほうのモヤモヤです)を感じることのできるセンスを維持することも大事ですので、そのための練習問題として、(上記の、ある特定の形式のオブジェクトのプロパティ比較と差分オブジェクトの作成が書けた後に、) 一般的なオブジェクトの比較という大きいテーマに、Lodash を使わないで取り組むのもよい修練になると思います。それに取り組むと、おそらく * stackoverflow: Generic deep diff between two objects についている回答のようなコードを書くことになり、JavaScriptの基礎確認に役立つと思います。 ### 3. 基礎を見直すことと応用的なものを使うことのバランスについて ここは意見の分かれるところだと思いますが、自分の考えを書いておきます。 私は「基礎がしっかり身についてないうちに、誰かが作ってくれた便利なものに頼ろうとしてはいけません。」というお説教を言うつもりは全くありません。むしろ逆で、基礎があやふやなうちから Lodash (に限らず JQuery でも、React でもいいのですが。) のような、言わば巷で流行の道具に目が行って、それに飛びつくことができるというのは、(これからもキープすべき)良質なセンスだと思います。それで飛びついてはみたものの、それを使ってみようとしたらうまくいかなくて、どうしてうまくいかないのかと試行錯誤した結果、「基礎力が足りない」と自分でちゃんと納得して結論することができ、その対策を(通常の業務時間とは別の時間を使うことになるかもしれませんが。)さっさと始めることが大事です。そのようにして、何か応用的なことをやろうとして、理解不足を感じた基礎項目に立ち返り、基礎を点検できたらまた応用に取り組むということを繰り返すうちに、基礎を見直すことと応用的な取り組みとの間の、自分なりのバランスが出来ていきます。 以上、参考になれば幸いです。