teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

2

おまけ追加

2017/12/10 07:45

投稿

miyabi-sun
miyabi-sun

スコア21449

answer CHANGED
@@ -92,4 +92,241 @@
92
92
  ```
93
93
 
94
94
  JavaScriptでは左辺がFalsy(if文に単体で突っ込んでfalseと判定される値、0とかnullとか)の場合、
95
- `a || b`と記載しておけば右辺を取得出来ます。
95
+ `a || b`と記載しておけば右辺を取得出来ます。
96
+
97
+ ---
98
+
99
+ 【おまけ】
100
+
101
+ コメント読みました、なるほど…確かに長く業務が動いていたりするとどっかにしわ寄せが行くケースがあります。
102
+ わかりました。勉強と実践で役立つ様に最終型も用意しておきました。
103
+ 最初からこの形式になってくれれば、めっちゃ使いやすいんじゃないですか?
104
+
105
+ ```JavaScript
106
+ { '1': { key: '1', name: '', path: [ '1' ], children: [ '1', '2' ] },
107
+ '2': { key: '2', name: '', path: [ '2' ], children: [ '1', '2' ] },
108
+ '10':
109
+ { key: '10',
110
+ name: '',
111
+ path: [ '1', '10' ],
112
+ children: [ '10', '15', '20' ] },
113
+ '15':
114
+ { key: '15',
115
+ name: '',
116
+ path: [ '1', '15' ],
117
+ children: [ '10', '15', '20' ] },
118
+ '20':
119
+ { key: '20',
120
+ name: '',
121
+ path: [ '1', '20' ],
122
+ children: [ '10', '15', '20' ] },
123
+ '21': { key: '21', name: '', path: [ '2', '21' ], children: [ '21' ] },
124
+ '1001':
125
+ { key: '1001',
126
+ name: '',
127
+ path: [ '1', '10', '1001' ],
128
+ children: [ '1001', '1002' ] },
129
+ '1002':
130
+ { key: '1002',
131
+ name: '',
132
+ path: [ '2', '21', '1002' ],
133
+ children: [ '1002', '2101' ] },
134
+ '2001':
135
+ { key: '2001',
136
+ name: '',
137
+ path: [ '1', '15', '2001' ],
138
+ children: [ '2001' ] },
139
+ '2006':
140
+ { key: '2006',
141
+ name: '',
142
+ path: [ '1', '20', '2006' ],
143
+ children: [ '2006' ] },
144
+ '2101':
145
+ { key: '2101',
146
+ name: '',
147
+ path: [ '2', '21', '2101' ],
148
+ children: [ '1002', '2101' ] },
149
+ '100101':
150
+ { key: '100101',
151
+ name: '',
152
+ path: [ '1', '10', '1001', '100101' ],
153
+ children: [ '100101', '100106' ] },
154
+ '100106':
155
+ { key: '100106',
156
+ name: '',
157
+ path: [ '1', '10', '1001', '100106' ],
158
+ children: [ '100101', '100106' ] },
159
+ '100201':
160
+ { key: '100201',
161
+ name: '',
162
+ path: [ '1', '10', '1002', '100201' ],
163
+ children: [ '100201' ] },
164
+ '10210101':
165
+ { key: '10210101',
166
+ name: '',
167
+ path: [ '2', '21', '2101', '10210101' ],
168
+ children: [ '10210101' ] } }
169
+ ```
170
+
171
+ ```Javascript
172
+ > console.log(pathByKey['1001'])
173
+ { key: '1001',
174
+ name: '',
175
+ path: [ '1', '10', '1001' ],
176
+ children: [ '1001', '1002' ] }
177
+
178
+ > console.log(pathByKey['100106'])
179
+ { key: '100106',
180
+ name: '',
181
+ path: [ '1', '10', '1001', '100106' ],
182
+ children: [ '100101', '100106' ] }
183
+
184
+ > console.log(pathByKey['100201'])
185
+ { key: '100201',
186
+ name: '',
187
+ path: [ '1', '10', '1002', '100201' ],
188
+ children: [ '100201' ] }
189
+
190
+ > console.log(pathByKey['1003'])
191
+ undefined
192
+
193
+ > console.log(pathByKey['10210101'])
194
+ { key: '10210101',
195
+ name: '',
196
+ path: [ '2', '21', '2101', '10210101' ],
197
+ children: [ '10210101' ] }
198
+ ```
199
+
200
+ 対応するコードがこれです。
201
+ おまけではこれを基準に解説します。
202
+ (多少読みやすくする為にリファクタリングも行っています)
203
+
204
+ ```JavaScript
205
+ var obj = {
206
+ "1": { "name": '', "child": {
207
+ "10": { "name": '', "child": {
208
+ "1001": { "name": '', "child": {
209
+ "100101": { "name": '', "child": {}},
210
+ "100106": { "name": '', "child": {}},
211
+ }},
212
+ "1002": { "name": '', "child": {
213
+ "100201": { "name": '', "child": {}}
214
+ }}
215
+ }},
216
+ "15": { "name": '', "child": {
217
+ "2001": { "name": '', "child": {}}
218
+ }},
219
+ "20": { "name": '', "child": {
220
+ "2006": { "name": '', "child": {}}
221
+ }}
222
+ }},
223
+ "2": { "name": '', "child": {
224
+ "21": { "name": '', "child": {
225
+ "2101": { "name": '', "child": {
226
+ "10210101": { "name": '', "child": {}}
227
+ }},
228
+ "1002": { "name": '', "child": {}}
229
+ }}
230
+ }}
231
+ };
232
+ var parse = (obj, path = []) =>
233
+ Object
234
+ .keys(obj)
235
+ .map(key => [
236
+ {
237
+ key: key,
238
+ name: obj[key].name,
239
+ path: [...path, key],
240
+ children: Object.keys(obj)
241
+ },
242
+ ...parse(obj[key].child, [...path, key])
243
+ ])
244
+ .reduce((arr, it) => [...arr, ...it], []);
245
+ var pathByKey = parse(obj).reduce((o, it) => {
246
+ o[it.key] = it;
247
+ return o;
248
+ }, {});
249
+ ```
250
+
251
+ ---
252
+
253
+ 今回利用したビルトイン関数は下記です。
254
+
255
+ - [Object.keys()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/keys)
256
+ - [Array.prototype.map()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
257
+ - [Array.prototype.reduce()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
258
+
259
+ > `a => a + 1`
260
+
261
+ この書き方はES2015で実装されたアロー関数です。
262
+ 例の`a => a + 1`は`function (a) { return a + 1; }`です。
263
+ (ただし、thisのスコープを新たに作らなかったり、aruguments変数を用意しなかったりと簡易的な関数として振る舞いますので、これが原因でハマる箇所もあります、ちらっと覚えておいてください)
264
+
265
+ > `[...path, key]`
266
+
267
+ この書き方もES2015で追加された書き方です。
268
+ 配列[]やオブジェクト{}の宣言時に頭に`...`を追加すると、展開してから追加の合図になります。
269
+
270
+ `path.push('123')`に似ていますね。
271
+ しかし、path.pushは破壊的なメソッドですので、
272
+ `path.push('123')`とするとpathという変数自身に改変が加わってしまいます。
273
+ このため、毎回新しくコピーを作り直すという目的で`path.push`の代わりに利用しています。
274
+
275
+ > Object.keys
276
+
277
+ 便利ですね。これはES5の機能なのでガンガン使いましょう。
278
+ JSに於ける`for...in`はprototypeも取ってくるので、
279
+ (アプリを作りたい一般エンジニアにとって)はゴミ以下の価値しかありません。
280
+ 今回は後述のリスト操作系のメソッドを利用したかったので採用しています。
281
+
282
+ > Array.prototype.map
283
+
284
+ 難解かつ記述量が一気に減るJS使いの必殺技その1です。
285
+ これは配列全ての要素に同じ関数を適用し、戻り値を元に配列を再構成するという機能になります。
286
+
287
+ 状態変数を作らずに記述出来るので、上手く使えば行数が減ったり簡素な書き方になることが期待出来ます。
288
+
289
+ > Array.prototype.reduce
290
+
291
+ 難解かつ記述量が一気に減るJS使いの必殺技その2です。
292
+ 第二引数の値を出発点として、配列の要素数同じ関数を実行して、戻り値を雪だるまのように転がしながら結果を得る手法です。
293
+ 「畳み込み」と呼ばれる手法ですね。
294
+
295
+ ```JavaScript
296
+ var add = (a, b) => a + b;
297
+ var sum = arr => arr.reduce(add, 0);
298
+ console.log(sum([1, 2, 3, 4]));
299
+ // 10
300
+ ```
301
+
302
+ - 1回目: 0(第二引数の初期値) + 1 -> 1
303
+ - 2回目: 1 + 2 -> 3
304
+ - 3回目: 3 + 3 -> 6
305
+ - 4回目: 6 + 4 -> 10
306
+
307
+ このような計算を裏で行った結果になります。
308
+ add関数を1行にすれば`sum = arr => arr.reduce((a, b) => a + b, 0)`と1行で簡単に宣言出来ますね。
309
+ 今回は2箇所でこのreduceを使っており、慣れるまでは難解なので解説します。
310
+
311
+ 1個目の使い方はobj.child.child.child...と掘っていくと、
312
+ 戻り値がどんどんネストしていってしまいます。
313
+ イメージとしてはこんな感じ。
314
+
315
+ ```JavaScript
316
+ var item = {key: "1010", path ["1", "10", "1010"]}; // objにアクセスするとこんな感じのデータが取得出来る
317
+ var children = [item, [item, [item, [item]]]]; // 再帰関数でどんどん掘っていくとこうなってしまう
318
+ ```
319
+
320
+ これではなんにも使えないですよね。
321
+ そこで、毎回第二引数以降のアイテムを一次元配列に引き戻しています。
322
+ 下記のようなイメージになります。
323
+
324
+ ```JavaScript
325
+ var item = {key: "1010", path ["1", "10", "1010"]}; // objにアクセスするとこんな感じのデータが取得出来る
326
+ var children = [[item], [item, item]; // ああ、ネストしてしまったやばい
327
+ var new_children = children.reduce((arr, it) => [...arr, ...it], []); // [item, item, item]
328
+ ```
329
+
330
+ 2個目の使い方は空のオブジェクトを宣言して、
331
+ 1次元配列のアイテムを次々と宣言してくっつけていっています。
332
+ こっちは上記の解説の応用ですんなり理解出来そうなので割愛します。

1

追記部分回答

2017/12/10 07:45

投稿

miyabi-sun
miyabi-sun

スコア21449

answer CHANGED
@@ -61,11 +61,35 @@
61
61
  '10210101': [ '2', '21', '2101', '10210101' ] }
62
62
  ```
63
63
 
64
- `15`キーの配下に'2001`が入っていたりしますが、これは探索木ですか?
64
+ `15`キーの配下に`2001`が入っていたりしますが、これは探索木ですか?
65
- 元々高速に探索する目的でしかこういうツリー構造では表現しないかと思うのですが、
66
- イレギュラーな値が多数含まれており高速に探索するとう目的では使えません。
65
+ もし探索木のつもりなら要件を満たしていません。
67
66
 
67
+ 結局全て開いてみる必要がありこれが正解であれば、
68
- 結局全て開いてみる必要があり、私の回答のようにに全て探索してインデックスを作っておくのが最善になるでしょう。
68
+ 私の回答のようにに全て探索してインデックスを作っておくのが最善になるでしょう。
69
69
 
70
70
  もし機密情報だったりして他人に理由を説明出来ないのであれば仕方ないですが、
71
- データ部をもう少しマシな設計に出来ないか検討してみてください。
71
+ データ部をもう少しマシな設計に出来ないか検討してみてください。
72
+
73
+ ---
74
+
75
+ 【追記】
76
+
77
+ > マッチしなかったらfalseやnullが返ってきてほしいです。
78
+
79
+ 上記のコードから進めます。
80
+
81
+ ```JavaScript
82
+ var pathByKey = parse(obj).reduce((path, it) => { path[it[it.length - 1]] = it; return path}, {});
83
+ console.log(pathByKey['1001']); // [ '1', '10', '1001' ]
84
+ console.log(pathByKey['100106']); // [ '1', '10', '1001', '100106' ]
85
+ console.log(pathByKey['100201']); // [ '1', '10', '1002', '100201' ]
86
+ console.log(pathByKey['1003']); // undefined
87
+ console.log(pathByKey['10210101']); // [ '2', '21', '2101', '10210101' ]
88
+
89
+ // falseやnullが返ってきて欲しいです。
90
+ console.log(pathByKey['1003'] || false); // false
91
+ console.log(pathByKey['1003'] || null); // null
92
+ ```
93
+
94
+ JavaScriptでは左辺がFalsy(if文に単体で突っ込んでfalseと判定される値、0とかnullとか)の場合、
95
+ `a || b`と記載しておけば右辺を取得出来ます。