質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
Lodash

Lodashは、JavaScriptのユーティリティライブラリ。Underscoreの派生ライブラリで、配列・オブジェクトの操作に便利です。また、コードの可読性も高めることができます。

Redux

Reduxは、JavaScriptアプリケーションの状態を管理するためのオープンソースライブラリです。ReactやAngularで一般的にユーザーインターフェイスの構築に利用されます。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

2回答

9396閲覧

2つのオブジェクトを比較して差分だけ取り出したい

moro123

総合スコア18

Lodash

Lodashは、JavaScriptのユーティリティライブラリ。Underscoreの派生ライブラリで、配列・オブジェクトの操作に便利です。また、コードの可読性も高めることができます。

Redux

Reduxは、JavaScriptアプリケーションの状態を管理するためのオープンソースライブラリです。ReactやAngularで一般的にユーザーインターフェイスの構築に利用されます。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2019/07/23 11:46

ReduxのstateとRedux-formのformValuesで状態管理してる入力フォームを更新する時に、入力変更前と変更後を比較して差分だけ取り出してpostをしたいと思ってます。
lodashのomitByを使うと差分のあるキーバリューを取り出せますが、オブジェクトの中にある配列(下記のサンプルだとcにあたります)だと
差分が無い全てのキーバリューも取得してしまいます。
現状の入力フォームの仕様は全てのキーバリューをセットして送信してしまうので、複数人で入力して保存すると上書き保存され入力された項目が消えてしまいます。
下記のようなオブジェクトを取り出すことは可能でしょうか。

js

1■入力変更前のstate 2let beforeData = { 3 a:1, 4 b:2, 5 c:[ 6 { d:3, e:4, f:5 }, 7 { g:6, h:7, i:8} 8 ], 9 m: 9 10}; 11■入力変更後のstate 12let afterData = { 13 a:1, 14 b:12, 15 c:[ 16 { d:3, e:4, f:5}, 17 { g:16, h:17, i:8} 18 { j:9, k:10, l:11} 19 ], 20 m: 12 21}; 22■差分だけ取り出したオブジェクト 23let postData = { 24 b:12, 25 c:[ 26 { g:16, h:17 }, 27 { j:9, k:10, l:11} 28 ], 29 m:12 30}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

こんにちは

ご質問に挙げられている beforeDataafterData との差分を、配列の各要素の差分も含めて取得するために、参考になりそうなコードがありましたので、以下それを回答します。

Reactで使えるコードは、以下のコメントで提示されています。

上記の関数 difference を使って、 postData を得るコードを試作したので、以下に挙げます。(※ひな形を create-react-app で作成しています。)

src/difference.js

javascript

1import { transform, isEqual, isObject } from 'lodash'; 2 3/** 4 * Deep diff between two object, using lodash 5 * @param {Object} object Object compared 6 * @param {Object} base Object to compare with 7 * @return {Object} Return a new object who represent the diff 8 */ 9function difference(object, base) { 10 return transform(object, (result, value, key) => { 11 if (!isEqual(value, base[key])) { 12 result[key] = isObject(value) && isObject(base[key]) ? difference(value, base[key]) : value; 13 } 14 }); 15} 16 17export default difference; 18

src/App.js

JSX

1import React from 'react'; 2import difference from './difference'; 3 4class App extends React.Component { 5 render() { 6 7 const beforeData = { 8 a: 1, 9 b: 2, 10 c: [ 11 {d: 3, e: 4, f: 5}, 12 {g: 6, h: 7, i: 8} 13 ], 14 m: 9 15 }; 16 17 const afterData = { 18 a: 1, 19 b: 12, 20 c: [ 21 {d: 3, e: 4, f: 5}, 22 {g: 16, h: 17, i: 8}, 23 {j: 9, k: 10, l: 11} 24 ], 25 m: 12 26 }; 27 28 const postData = difference(afterData, beforeData); 29 30 return ( 31 <pre> 32 {JSON.stringify(postData, null, "\t")} 33 </pre> 34 ); 35 } 36} 37 38export default App;

src/index.js

import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; ReactDOM.render(<App />, document.getElementById('root'));

public/index.html

<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8" /> <title>Q202169</title> </head> <body> <div id="root"></div> </body> </html>

package.json

json

1{ 2 "name": "q202169", 3 "version": "1.0", 4 "private": true, 5 "dependencies": { 6 "lodash": "^4.17.15", 7 "react": "^16.8.6", 8 "react-dom": "^16.8.6", 9 "react-scripts": "3.0.1" 10 }, 11 "scripts": { 12 "start": "react-scripts start" 13 }, 14 "eslintConfig": { 15 "extends": "react-app" 16 }, 17 "browserslist": { 18 "production": [ 19 ">0.2%", 20 "not dead", 21 "not op_mini all" 22 ], 23 "development": [ 24 "last 1 chrome version", 25 "last 1 firefox version", 26 "last 1 safari version" 27 ] 28 } 29} 30

差分をPOSTで送るとのことでしたので、上記の App では postData をJSON にして表示しています。上記を yarn startすると以下のように表示されました。

イメージ説明

上記を見る限りでは望ましい結果になっているかと思います。(プロパティcの配列の先頭要素は差分が無いので、 null になっているのは妥当な結果と思われます)

私自身は difference のコードをあまり精査しておりませんので、お使いになる場合は何パターンかテストされることをお勧めします。

以上、参考になれば幸いです。

投稿2019/07/23 13:34

編集2019/07/24 20:58
jun68ykt

総合スコア9058

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

オブジェクトの差分比較

下記のようにコードを書けば、「追加」「変更」「削除」の全てに対応可能ですが、

JavaScript

1for (let key of Object.keys(afterData)) { 2 // in 演算子、プロパティアクセサで比較 3} 4 5for (let key of Object.keys(beforeData)) { 6 // in 演算子、プロパティアクセサで比較 7}

defaultValue との比較

現状の入力フォームの仕様は全てのキーバリューをセットして送信してしまうので、複数人で入力して保存すると上書き保存され入力された項目が消えてしまいます。

この様子ですと、submit直前に変更前を得るのではなく、ページ描画時の変更前値をそのまま使うのですよね?
(変更された値は上書きされるが、変更しなかった値は上書きされない)

この仕様にするなら、input要素やtextarea要素側で defaultValue プロパティと比較する方がスマートかと思います。
select要素には初期値取得の機構がないので、初回表示時に data-defaultSelectedIndex に埋め込んでおく等の工夫が必要ですが。

  • input イベントで監視 -> 動的に差分比較
  • submit イベントで監視 -> 最期にまとめて差分比較

上書き前に確認する

前述の仕様では、変更範囲が限定されるとはいえ、上書きされる問題は残っています。
理想的には、下記動作になると思います。

  • 「submit直前の変更前値の更新日時」と「現行の変更前値の更新日時」を比較して、相違なければsubmit
  • 相違あれば、変更前値を別の場所に読み込み、「変更前値をリロードして編集を継続するか」をユーザに尋ねる

Re: moro123 さん

投稿2019/07/23 12:07

編集2019/07/23 23:54
think49

総合スコア18189

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

moro123

2019/07/23 15:29 編集

think49さん ご回答ありがとうございます。 for ofを使ってin 演算子、プロパティアクセサで比較とご回答いただけたのですが、cの配列の中にあるオブジェクトの比較がうまくいかないのですが具体的にどういう記述で比較すればよろしいのでしょうか。
think49

2019/07/24 23:15 編集

To: moro123 さん > の配列の中にあるオブジェクトの比較がうまくいかないのですが 再帰処理を使いますが、全てのObject型を再帰させると望まない結果を生むので、Object.prototype.toString.call(afterData[key]) === '[object Object]' のように判定して、再帰呼び出しします。 私の回答の本筋は代替案(defaultValue と「上書き前に確認する」)にあるので、そちらの見解が気になるところです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問