前提・実現したいこと
※初心者ながら自分なりにコードを書いて目的(やりたいこと)は達成できています。
具体的なモデルがあったほうがわかりやすいと思うので、まずそちらを共有させていただきます。
以下サロン予約サイトです。
https://beauty.hotpepper.jp/slnH000314178/coupon/
サロン予約サイトに掲載されているサロンは複数のメニュー(子)を持っています。
さらに、そのメニューは、複数のオプション(孫)を持っています。このことから
サロンのメニューは複数のオプション(子)を持っている = サロンは複数のオプション(孫)を持っている
と定義出来るかと思います。
システム的なマスタデータは後者のサロンのオプションとなります。
データストアはRDBでなくNoSQLなので、冗長(非正規)な構成となり、参照カウント(何個のメニューに参照されているか)をマスタデータに持っています。
これがシステム上で扱うデータモデルの前提となります。
ここでイメージ図です。
- サロンのメニューは複数のオプション(子)を持っている
サロン ├ メニューA │ ├ オプションA │ └ オプションB ├ メニューB │ ├ オプションA │ ├ オプションB │ └ オプションC ︙
- サロンは複数のオプション(孫)を持っている
サロン ├ オプションA、参照カウント=2(2つのメニューに参照されている状態) ├ オプションB、参照カウント=2(2つのメニューに参照されている状態) └ オプションC、参照カウント=1(1つのメニューに参照されている状態)
オプションの具体的な例としては、カラー(髪を染める)というメニューであれば、金髪、茶髪等の選択肢が該当します。
そして、上記データモデルを前提とした**システム的な主要件は「メニュー登録・編集画面でオプションを追加・更新・削除できて、それがマスタデータと同期されていること」**です。
つまり、メニュー登録・編集画面で以下のようなことを実現したいです。
- サロンのオプションからオプションを選択して、メニューのオプションに追加できる
その際、マスタデータの参照カウントをインクリメントする。
※今回こちらに該当するコードは除外しています
- サロンのオプションに希望のオプションがなければ、メニューのオプションとして新規作成・追加できる。
その際、新規作成したオプションを参照カウント=1としてマスタデータにも反映する。
- メニューのオプションを更新できる。
その際、マスタデータにその更新後の値が存在しなければ、メニューのオプションとして新規作成・追加し参照カウント=1としてマスタデータにも反映する。
- メニューのオプションを削除できる。
その際、他メニューから参照されていなければ、マスタデータからも削除する。他メニューから参照されていれば、マスタデータの参照カウントをデクリメントする。
あとは、ソースコード見ながらのほうが説明しやすいので、ところどころソースコード中にコメントを入れてます。
発生している問題・エラーメッセージ
以下期待値を得るため、ライブラリ等使わずJavascriptのみでコーディングを試みましたが、悪戦苦闘した結果、ライブラリ(Lodash)を使うことにしました。
なんとか自力で書いた(自分の中で最善)コードを書きましたが、もっとスマートかつ高パフォーマンスに以下の期待値を得たいです。
周りにレビュアーがおらず、Teratailだけが頼りです。
Javascript
1// 期待値 2const expectedSalon = { 3 name: 'い~サロン', 4 menus: [ 5 { 6 name: 'カット', 7 // サロンのメニューは複数のオプション(子)を持っている 8 options: [ 9 {name: 'オプション1', price: 100}, 10 {name: 'オプション2', price: 200}, 11 {name: 'オプション3', price: 300} 12 ] 13 // ︙(省略) 14 }, 15 { 16 name: 'カラー', 17 // サロンのメニューは複数のオプション(子)を持っている 18 options: [ 19 {name: 'オプション1', price: 100}, 20 {name: 'オプション2', price: 222}, 21 {name: 'オプション3', price: 300}, 22 {name: 'オプション5', price: 500} 23 ] 24 // ︙(省略) 25 } 26 ], 27 // サロンは複数のオプション(孫)を持っている 28 menuOptions: [ 29 {name: 'オプション1', price: 100, refCount: 2}, 30 {name: 'オプション2', price: 200, refCount: 1}, 31 {name: 'オプション2', price: 222, refCount: 1}, 32 {name: 'オプション3', price: 300, refCount: 1}, 33 {name: 'オプション5', price: 500, refCount: 1}, 34 ] 35 // ︙(省略) 36};
該当のソースコード
Javascript
1// 初期(更新前)データ 2const salon = { 3 name: 'い~サロン', 4 menus: [ 5 { 6 name: 'カット', 7 // サロンのメニューは複数のオプション(子)を持っている 8 options: [ 9 {name: 'オプション1', price: 100}, 10 {name: 'オプション2', price: 200}, 11 {name: 'オプション3', price: 300} 12 ] 13 // ︙(省略) 14 }, 15 { 16 name: 'カラー', 17 // サロンのメニューは複数のオプション(子)を持っている 18 options: [ 19 {name: 'オプション1', price: 100}, 20 {name: 'オプション2', price: 200}, 21 {name: 'オプション3', price: 300}, 22 {name: 'オプション4', price: 400} 23 ] 24 // ︙(省略) 25 } 26 ], 27 // サロンは複数のオプション(孫)を持っている 28 menuOptions: [ 29 {name: 'オプション1', price: 100, refCount: 2}, 30 {name: 'オプション2', price: 200, refCount: 2}, 31 {name: 'オプション3', price: 300, refCount: 2}, 32 {name: 'オプション4', price: 400, refCount: 1} 33 ] 34 // ︙(省略) 35}; 36 37// カラーメニューを画面から更新(=Postされたデータ) 38// 更新内容: オプション1はそのまま、オプション2を更新、オプション3とオプション4を削除、オプション5が2つ追加 39const updatedColorMenu = { 40 name:'カラー', 41 options: [ 42 {name: 'オプション1', price: 100}, 43 {name: 'オプション2', price: 222}, 44 {name: 'オプション5', price: 500}, 45 {name: 'オプション5', price: 500} 46 ] 47 // ︙(省略) 48} 49 50// ↓↓↓↓↓以降がPost後の処理↓↓↓↓↓ 51// 追加されたオプションを抽出 52const addedOptions = _.uniqWith( 53 _.differenceWith(updatedColorMenu.options, salon.menuOptions, (a, b) => { 54 return _.isEqual( 55 a, 56 _.omit(b, ['refCount']) 57 ); 58 } 59), _.isEqual); 60console.log(addedOptions); 61 62// 削除されたオプションを抽出 63const removedOptions = _.differenceWith(salon.menuOptions, updatedColorMenu.options, (a, b) => { 64 return _.isEqual( 65 _.omit(a, ['refCount']), 66 b 67 ); 68 } 69); 70console.log(removedOptions); 71 72// マスターとなるオプションを生成 73let masterOptions = []; 74// 削除されたオプションをマスターから削除 75salon.menuOptions.forEach(x => { 76 if (_.some(removedOptions, x)) { 77 if (x.refCount > 1) { 78 x.refCount = x.refCount - 1; 79 masterOptions.push(x); 80 } 81 } else { 82 masterOptions.push(x); 83 } 84}); 85// 追加されたオプションをマスターに追加 86addedOptions.forEach(x => { 87 x.refCount = 1; 88 masterOptions.push(x); 89}); 90 91console.log(masterOptions);
試したこと
上記の通り、何とか自力で期待値と一致させることができました。
補足情報(FW/ツールのバージョンなど)
Lodashを使っています。
余分なプロパティは可読性の観点から省略しています。
データストアはRDBでなくNoSQLです
回答1件
あなたの回答
tips
プレビュー