回答編集履歴

2

おまけ追加

2017/12/10 07:45

投稿

miyabi-sun
miyabi-sun

スコア21158

test CHANGED
@@ -187,3 +187,477 @@
187
187
  JavaScriptでは左辺がFalsy(if文に単体で突っ込んでfalseと判定される値、0とかnullとか)の場合、
188
188
 
189
189
  `a || b`と記載しておけば右辺を取得出来ます。
190
+
191
+
192
+
193
+ ---
194
+
195
+
196
+
197
+ 【おまけ】
198
+
199
+
200
+
201
+ コメント読みました、なるほど…確かに長く業務が動いていたりするとどっかにしわ寄せが行くケースがあります。
202
+
203
+ わかりました。勉強と実践で役立つ様に最終型も用意しておきました。
204
+
205
+ 最初からこの形式になってくれれば、めっちゃ使いやすいんじゃないですか?
206
+
207
+
208
+
209
+ ```JavaScript
210
+
211
+ { '1': { key: '1', name: '', path: [ '1' ], children: [ '1', '2' ] },
212
+
213
+ '2': { key: '2', name: '', path: [ '2' ], children: [ '1', '2' ] },
214
+
215
+ '10':
216
+
217
+ { key: '10',
218
+
219
+ name: '',
220
+
221
+ path: [ '1', '10' ],
222
+
223
+ children: [ '10', '15', '20' ] },
224
+
225
+ '15':
226
+
227
+ { key: '15',
228
+
229
+ name: '',
230
+
231
+ path: [ '1', '15' ],
232
+
233
+ children: [ '10', '15', '20' ] },
234
+
235
+ '20':
236
+
237
+ { key: '20',
238
+
239
+ name: '',
240
+
241
+ path: [ '1', '20' ],
242
+
243
+ children: [ '10', '15', '20' ] },
244
+
245
+ '21': { key: '21', name: '', path: [ '2', '21' ], children: [ '21' ] },
246
+
247
+ '1001':
248
+
249
+ { key: '1001',
250
+
251
+ name: '',
252
+
253
+ path: [ '1', '10', '1001' ],
254
+
255
+ children: [ '1001', '1002' ] },
256
+
257
+ '1002':
258
+
259
+ { key: '1002',
260
+
261
+ name: '',
262
+
263
+ path: [ '2', '21', '1002' ],
264
+
265
+ children: [ '1002', '2101' ] },
266
+
267
+ '2001':
268
+
269
+ { key: '2001',
270
+
271
+ name: '',
272
+
273
+ path: [ '1', '15', '2001' ],
274
+
275
+ children: [ '2001' ] },
276
+
277
+ '2006':
278
+
279
+ { key: '2006',
280
+
281
+ name: '',
282
+
283
+ path: [ '1', '20', '2006' ],
284
+
285
+ children: [ '2006' ] },
286
+
287
+ '2101':
288
+
289
+ { key: '2101',
290
+
291
+ name: '',
292
+
293
+ path: [ '2', '21', '2101' ],
294
+
295
+ children: [ '1002', '2101' ] },
296
+
297
+ '100101':
298
+
299
+ { key: '100101',
300
+
301
+ name: '',
302
+
303
+ path: [ '1', '10', '1001', '100101' ],
304
+
305
+ children: [ '100101', '100106' ] },
306
+
307
+ '100106':
308
+
309
+ { key: '100106',
310
+
311
+ name: '',
312
+
313
+ path: [ '1', '10', '1001', '100106' ],
314
+
315
+ children: [ '100101', '100106' ] },
316
+
317
+ '100201':
318
+
319
+ { key: '100201',
320
+
321
+ name: '',
322
+
323
+ path: [ '1', '10', '1002', '100201' ],
324
+
325
+ children: [ '100201' ] },
326
+
327
+ '10210101':
328
+
329
+ { key: '10210101',
330
+
331
+ name: '',
332
+
333
+ path: [ '2', '21', '2101', '10210101' ],
334
+
335
+ children: [ '10210101' ] } }
336
+
337
+ ```
338
+
339
+
340
+
341
+ ```Javascript
342
+
343
+ > console.log(pathByKey['1001'])
344
+
345
+ { key: '1001',
346
+
347
+ name: '',
348
+
349
+ path: [ '1', '10', '1001' ],
350
+
351
+ children: [ '1001', '1002' ] }
352
+
353
+
354
+
355
+ > console.log(pathByKey['100106'])
356
+
357
+ { key: '100106',
358
+
359
+ name: '',
360
+
361
+ path: [ '1', '10', '1001', '100106' ],
362
+
363
+ children: [ '100101', '100106' ] }
364
+
365
+
366
+
367
+ > console.log(pathByKey['100201'])
368
+
369
+ { key: '100201',
370
+
371
+ name: '',
372
+
373
+ path: [ '1', '10', '1002', '100201' ],
374
+
375
+ children: [ '100201' ] }
376
+
377
+
378
+
379
+ > console.log(pathByKey['1003'])
380
+
381
+ undefined
382
+
383
+
384
+
385
+ > console.log(pathByKey['10210101'])
386
+
387
+ { key: '10210101',
388
+
389
+ name: '',
390
+
391
+ path: [ '2', '21', '2101', '10210101' ],
392
+
393
+ children: [ '10210101' ] }
394
+
395
+ ```
396
+
397
+
398
+
399
+ 対応するコードがこれです。
400
+
401
+ おまけではこれを基準に解説します。
402
+
403
+ (多少読みやすくする為にリファクタリングも行っています)
404
+
405
+
406
+
407
+ ```JavaScript
408
+
409
+ var obj = {
410
+
411
+ "1": { "name": '', "child": {
412
+
413
+ "10": { "name": '', "child": {
414
+
415
+ "1001": { "name": '', "child": {
416
+
417
+ "100101": { "name": '', "child": {}},
418
+
419
+ "100106": { "name": '', "child": {}},
420
+
421
+ }},
422
+
423
+ "1002": { "name": '', "child": {
424
+
425
+ "100201": { "name": '', "child": {}}
426
+
427
+ }}
428
+
429
+ }},
430
+
431
+ "15": { "name": '', "child": {
432
+
433
+ "2001": { "name": '', "child": {}}
434
+
435
+ }},
436
+
437
+ "20": { "name": '', "child": {
438
+
439
+ "2006": { "name": '', "child": {}}
440
+
441
+ }}
442
+
443
+ }},
444
+
445
+ "2": { "name": '', "child": {
446
+
447
+ "21": { "name": '', "child": {
448
+
449
+ "2101": { "name": '', "child": {
450
+
451
+ "10210101": { "name": '', "child": {}}
452
+
453
+ }},
454
+
455
+ "1002": { "name": '', "child": {}}
456
+
457
+ }}
458
+
459
+ }}
460
+
461
+ };
462
+
463
+ var parse = (obj, path = []) =>
464
+
465
+ Object
466
+
467
+ .keys(obj)
468
+
469
+ .map(key => [
470
+
471
+ {
472
+
473
+ key: key,
474
+
475
+ name: obj[key].name,
476
+
477
+ path: [...path, key],
478
+
479
+ children: Object.keys(obj)
480
+
481
+ },
482
+
483
+ ...parse(obj[key].child, [...path, key])
484
+
485
+ ])
486
+
487
+ .reduce((arr, it) => [...arr, ...it], []);
488
+
489
+ var pathByKey = parse(obj).reduce((o, it) => {
490
+
491
+ o[it.key] = it;
492
+
493
+ return o;
494
+
495
+ }, {});
496
+
497
+ ```
498
+
499
+
500
+
501
+ ---
502
+
503
+
504
+
505
+ 今回利用したビルトイン関数は下記です。
506
+
507
+
508
+
509
+ - [Object.keys()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/keys)
510
+
511
+ - [Array.prototype.map()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
512
+
513
+ - [Array.prototype.reduce()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
514
+
515
+
516
+
517
+ > `a => a + 1`
518
+
519
+
520
+
521
+ この書き方はES2015で実装されたアロー関数です。
522
+
523
+ 例の`a => a + 1`は`function (a) { return a + 1; }`です。
524
+
525
+ (ただし、thisのスコープを新たに作らなかったり、aruguments変数を用意しなかったりと簡易的な関数として振る舞いますので、これが原因でハマる箇所もあります、ちらっと覚えておいてください)
526
+
527
+
528
+
529
+ > `[...path, key]`
530
+
531
+
532
+
533
+ この書き方もES2015で追加された書き方です。
534
+
535
+ 配列[]やオブジェクト{}の宣言時に頭に`...`を追加すると、展開してから追加の合図になります。
536
+
537
+
538
+
539
+ `path.push('123')`に似ていますね。
540
+
541
+ しかし、path.pushは破壊的なメソッドですので、
542
+
543
+ `path.push('123')`とするとpathという変数自身に改変が加わってしまいます。
544
+
545
+ このため、毎回新しくコピーを作り直すという目的で`path.push`の代わりに利用しています。
546
+
547
+
548
+
549
+ > Object.keys
550
+
551
+
552
+
553
+ 便利ですね。これはES5の機能なのでガンガン使いましょう。
554
+
555
+ JSに於ける`for...in`はprototypeも取ってくるので、
556
+
557
+ (アプリを作りたい一般エンジニアにとって)はゴミ以下の価値しかありません。
558
+
559
+ 今回は後述のリスト操作系のメソッドを利用したかったので採用しています。
560
+
561
+
562
+
563
+ > Array.prototype.map
564
+
565
+
566
+
567
+ 難解かつ記述量が一気に減るJS使いの必殺技その1です。
568
+
569
+ これは配列全ての要素に同じ関数を適用し、戻り値を元に配列を再構成するという機能になります。
570
+
571
+
572
+
573
+ 状態変数を作らずに記述出来るので、上手く使えば行数が減ったり簡素な書き方になることが期待出来ます。
574
+
575
+
576
+
577
+ > Array.prototype.reduce
578
+
579
+
580
+
581
+ 難解かつ記述量が一気に減るJS使いの必殺技その2です。
582
+
583
+ 第二引数の値を出発点として、配列の要素数同じ関数を実行して、戻り値を雪だるまのように転がしながら結果を得る手法です。
584
+
585
+ 「畳み込み」と呼ばれる手法ですね。
586
+
587
+
588
+
589
+ ```JavaScript
590
+
591
+ var add = (a, b) => a + b;
592
+
593
+ var sum = arr => arr.reduce(add, 0);
594
+
595
+ console.log(sum([1, 2, 3, 4]));
596
+
597
+ // 10
598
+
599
+ ```
600
+
601
+
602
+
603
+ - 1回目: 0(第二引数の初期値) + 1 -> 1
604
+
605
+ - 2回目: 1 + 2 -> 3
606
+
607
+ - 3回目: 3 + 3 -> 6
608
+
609
+ - 4回目: 6 + 4 -> 10
610
+
611
+
612
+
613
+ このような計算を裏で行った結果になります。
614
+
615
+ add関数を1行にすれば`sum = arr => arr.reduce((a, b) => a + b, 0)`と1行で簡単に宣言出来ますね。
616
+
617
+ 今回は2箇所でこのreduceを使っており、慣れるまでは難解なので解説します。
618
+
619
+
620
+
621
+ 1個目の使い方はobj.child.child.child...と掘っていくと、
622
+
623
+ 戻り値がどんどんネストしていってしまいます。
624
+
625
+ イメージとしてはこんな感じ。
626
+
627
+
628
+
629
+ ```JavaScript
630
+
631
+ var item = {key: "1010", path ["1", "10", "1010"]}; // objにアクセスするとこんな感じのデータが取得出来る
632
+
633
+ var children = [item, [item, [item, [item]]]]; // 再帰関数でどんどん掘っていくとこうなってしまう
634
+
635
+ ```
636
+
637
+
638
+
639
+ これではなんにも使えないですよね。
640
+
641
+ そこで、毎回第二引数以降のアイテムを一次元配列に引き戻しています。
642
+
643
+ 下記のようなイメージになります。
644
+
645
+
646
+
647
+ ```JavaScript
648
+
649
+ var item = {key: "1010", path ["1", "10", "1010"]}; // objにアクセスするとこんな感じのデータが取得出来る
650
+
651
+ var children = [[item], [item, item]; // ああ、ネストしてしまったやばい
652
+
653
+ var new_children = children.reduce((arr, it) => [...arr, ...it], []); // [item, item, item]
654
+
655
+ ```
656
+
657
+
658
+
659
+ 2個目の使い方は空のオブジェクトを宣言して、
660
+
661
+ 1次元配列のアイテムを次々と宣言してくっつけていっています。
662
+
663
+ こっちは上記の解説の応用ですんなり理解出来そうなので割愛します。

1

追記部分回答

2017/12/10 07:45

投稿

miyabi-sun
miyabi-sun

スコア21158

test CHANGED
@@ -124,18 +124,66 @@
124
124
 
125
125
 
126
126
 
127
- `15`キーの配下に'2001`が入っていたりしますが、これは探索木ですか?
127
+ `15`キーの配下に`2001`が入っていたりしますが、これは探索木ですか?
128
128
 
129
- 元々高速に探索する目的でしかこういうツリー構造では表現しないかと思うのですが、
130
-
131
- イレギュラー値が多数含まれおり高速に探索するとう目的では使えません。
129
+ もし探索木のつもりら要件を満たしていません。
132
130
 
133
131
 
134
132
 
133
+ 結局全て開いてみる必要がありこれが正解であれば、
134
+
135
- 結局全て開いてみる必要があり、私の回答のようにに全て探索してインデックスを作っておくのが最善になるでしょう。
135
+ 私の回答のようにに全て探索してインデックスを作っておくのが最善になるでしょう。
136
136
 
137
137
 
138
138
 
139
139
  もし機密情報だったりして他人に理由を説明出来ないのであれば仕方ないですが、
140
140
 
141
141
  データ部をもう少しマシな設計に出来ないか検討してみてください。
142
+
143
+
144
+
145
+ ---
146
+
147
+
148
+
149
+ 【追記】
150
+
151
+
152
+
153
+ > マッチしなかったらfalseやnullが返ってきてほしいです。
154
+
155
+
156
+
157
+ 上記のコードから進めます。
158
+
159
+
160
+
161
+ ```JavaScript
162
+
163
+ var pathByKey = parse(obj).reduce((path, it) => { path[it[it.length - 1]] = it; return path}, {});
164
+
165
+ console.log(pathByKey['1001']); // [ '1', '10', '1001' ]
166
+
167
+ console.log(pathByKey['100106']); // [ '1', '10', '1001', '100106' ]
168
+
169
+ console.log(pathByKey['100201']); // [ '1', '10', '1002', '100201' ]
170
+
171
+ console.log(pathByKey['1003']); // undefined
172
+
173
+ console.log(pathByKey['10210101']); // [ '2', '21', '2101', '10210101' ]
174
+
175
+
176
+
177
+ // falseやnullが返ってきて欲しいです。
178
+
179
+ console.log(pathByKey['1003'] || false); // false
180
+
181
+ console.log(pathByKey['1003'] || null); // null
182
+
183
+ ```
184
+
185
+
186
+
187
+ JavaScriptでは左辺がFalsy(if文に単体で突っ込んでfalseと判定される値、0とかnullとか)の場合、
188
+
189
+ `a || b`と記載しておけば右辺を取得出来ます。