再帰的なマージが知りたいという知的好奇心寄りな動機で、
テストデータを適当に作って用意してきただけだと思います。
一応おまけとして下に追加しますが、現実的にはそんな用途はほぼありません。
何故ならデータが汚すぎるからです。
IDとstatusと各種フルーツの名前が一つのオブジェクトにある時点でおぞましい……
しかし我々ITエンジニアの仕事は情報処理です。
こんなぐちゃぐちゃなデータじゃバグも大量に出ますし、
コードも無駄に増えますから相当読みづらくなるでしょう。
逆に言えばデータにちゃんと指向性をもたせれば今回のような要望も99%程度の割合で抑え込めるのです。
段取り八分はITエンジニアも同様です。
Array.prototype.mapやLocashなんかも駆使すればいくらでもデータは加工出来るわけですからね。
JavaScript
1const fruitList = {
2 status :"B",
3};
4const fruits = [
5 {name: "apple", color: "red"},
6 {name: "banana", color: "yellow"},
7 {name: "strawberry", color: "red"}
8]; // オブジェクトの配列は凄まじく扱い安く効率が良い
9
10// ああ、アイテム2がこれだったらどんなに楽だっただろうか……
11const fruitListId = 21;
12const fruitIdList = {
13 apple: 13,
14 banana: 12,
15 strawberry: 17
16};
17
18const result = {
19 ...fruitList,
20 id: fruitListId,
21 fruits: fruits.map(fruit => ({
22 id: fruitIdList[fruit.name],
23 ...fruit,
24 }))
25}
26console.log(JSON.stringify(result, null, 2));
結果
JSON
1{
2 "status": "B",
3 "id": 21,
4 "fruits": [
5 {
6 "id": 13,
7 "name": "apple",
8 "color": "red"
9 },
10 {
11 "id": 12,
12 "name": "banana",
13 "color": "yellow"
14 },
15 {
16 "id": 17,
17 "name": "strawberry",
18 "color": "red"
19 }
20 ]
21}
それでも初期でもらえるデータ型がitemとitem2しかないという前提ならば
頑張って上記のデータになるように加工するのが最も楽です。
JavaScript
1const item = {
2 fruits: {
3 status :"B",
4 apple: {
5 color: "red"
6 },
7 banana: {
8 color: "yellow"
9 },
10 strawberry: {
11 color: "red"
12 }
13 }
14};
15const item2 = {
16 fruits: {
17 id: 21,
18 status :"C",
19 apple: {
20 id: 13,
21 color: "blue"
22 },
23 banana: {
24 id: 12,
25 color: "green"
26 },
27 strawberry: {
28 id: 17,
29 color: "red"
30 }
31 }
32};
33const itemColumns = ["id", "status"];
34
35const fruitsList = Object.fromEntries(
36 Object
37 .entries(item.fruits)
38 .filter(([key]) => itemColumns.includes(key))
39);
40// {status: "B"}
41
42const fruites = Object.entries(item.fruits)
43 .filter(([key]) => !itemColumns.includes(key))
44 .map(([key, values]) => ({_name: key, ...values}));
45// [
46// {
47// "_name": "apple",
48// "color": "red"
49// },
50// {
51// "_name": "banana",
52// "color": "yellow"
53// },
54// {
55// "_name": "strawberry",
56// "color": "red"
57// }
58// ]
59
60const fruitIds = Object.fromEntries(
61 Object.entries(item2.fruits)
62 .filter(([key]) => !itemColumns.includes(key))
63 .map(([key, val]) => [key, val.id])
64);
65// {apple: 13, banana: 12, strawberry: 17}
66
67const item3 = {
68 id: item2.fruits.id,
69 ...fruitsList,
70 ...Object.fromEntries(
71 fruites.map(fruit => [
72 fruit._name,
73 Object.fromEntries([
74 ['id', fruitIds[fruit._name]],
75 ...Object.entries(fruit).filter(([key]) => key != '_name')
76 ])
77 ])
78 )
79}
80console.log(JSON.stringify(item3, null, 2));
JSON
1{
2 "id": 21,
3 "status": "B",
4 "apple": {
5 "id": 13,
6 "color": "red"
7 },
8 "banana": {
9 "id": 12,
10 "color": "yellow"
11 },
12 "strawberry": {
13 "id": 17,
14 "color": "red"
15 }
16}
【おまけ】 機械的にマージすれば良いだけなら
Lodashのgetとsetを覚えるのが先ですかね。
Online Lodash TesterというサイトでLodashの動作を確認出来ます
JavaScript
1result = _.set({}, 'a.b.c', 123);
2// {"a": {"b": {"c": 123}}}
JavaScriptは全ての型でプロパティにアクセス出来ますが、
null
とundefined
のみプロパティを所持することもアクセスを試みることも不可能でエラーになってしまいます。
なのでキーをネストさせる場合はいちいちobj.a = {}; obj.a.b = {}; obj.a.b.c = 123;
みたいに書かなければならないのですが、
Lodashのsetはその辺に対応しているわけですね。
後は再帰的に掘ってキーを見つける関数を用意するだけです。
JavaScript
1const isObject = (obj, key) => {
2 if (_.isArray(obj[key])) return false;
3 if (_.isObject(obj[key])) return true;
4 return false;
5}
6const recursiveKeys = obj => [
7 ...Object.keys(obj).filter(key => !isObject(obj, key)),
8 ..._.flatten(
9 Object.keys(obj)
10 .filter(key => isObject(obj, key))
11 .map(key => recursiveKeys(obj[key]).map(it => `${key}.${it}`))
12 )
13]
14const item = {
15 fruits: {
16 status :"B",
17 apple: {
18 color: "red"
19 },
20 banana: {
21 color: "yellow"
22 },
23 strawberry: {
24 color: "red"
25 }
26 }
27};
28result = recursiveKeys(item);
JSON
1[
2 "fruits.status",
3 "fruits.apple.color",
4 "fruits.banana.color",
5 "fruits.strawberry.color"
6]
同じようにitem2を調査したらご覧のような結果に
JSON
1[
2 "fruits.id",
3 "fruits.status",
4 "fruits.apple.id",
5 "fruits.apple.color",
6 "fruits.banana.id",
7 "fruits.banana.color",
8 "fruits.strawberry.id",
9 "fruits.strawberry.color"
10]
後は空のオブジェクト作って、
for文かなんかでitem2
→item
の順番で_.set
を使って適用していくだけで完成します。
ただまぁ、オブジェクト判定の辺りが私の作った関数は手抜きして作ったレベルなので、
商用利用するレベルになると全然足りません。
Date
型のインスタンスとか混じると空中分解すると思います。
なので全くおすすめしませんし、保証も出来ません。
従って所詮おまけレベルと考えて、再帰的なマージよりはきっちりデータ型を取り決めて
綺麗なデータでやり取りするべきでしょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。