Help us understand the problem. What is going on with this article?

フロントエンド2年目のエンジニアが再帰処理を必要にかられて使えるようになったお話

パーソンリンクアドベントカレンダー 10日目 & Qiita初投稿です!
エンジニアになって2年目のhanetukiです。
現在は、フロントエンドメインで開発して飯を食べてます。そんな私の備考録です。

出会いは突然に

[
  { name: "タカシ",
    children: [
      { name: "ミツル" },
      { name: "ナツキ",
        children: [
          { name: "アイナ" },
          { name: "ナオキ" }
        ]
      },
      { name: "ヨツハ" },
      { name: "フタバ",
        children: [
          { name: "リク" }
        ]
      }
    ]
  },
  { name: "ソラ",
    children: [
      { name: "ハナ" }
    ]
  }
]

上記のような階層構造を処理する際に、ユニークになるkeyが一つも存在しないので何か付与する必要がありました。
ユニークになるkey無いと、対象となる値の更新や参照が困難になる為です。

JavaScriptでオブジェクトデータを扱う(登録・参照・更新・削除)
(...こんな感じのことをやるのにユニークになるkeyが必要だった)

そのときたどり着いたのが親子関係がはっきりしているパス情報を作成することでした。

よしメソッド作ろう!と意気込んだものの...階層構造のネストがどこ続いていなかったりどこまでも続いていたり、
この手のJSONを処理するのは初めてでした。

(入れ子 尽きるまで object js)検索

我ながら頭の悪そうな検索ワードでGoogle先生に尋ねたところ
再帰関数 なるものに出会いました。

再帰関数ってなんなんだ??
再帰関数を学ぶと、どんな世界が広がるか

つまりはこうだ、

// 関数をconstで定義する
const func = function(count) {
  console.log(`console: ${count}`);

  if (count) {
    // 関数の中でconstで定義したfuncを呼び出す。
    func(count--);
  }
};

// 初期値を引数に渡す
func(10);

// 結果
// console: 10
// console: 9
// console: 8
// console: 7
// console: 6
// console: 5
// console: 4
// console: 3
// console: 2
// console: 1
// console: 0

関数の中でその関数を呼び出すことでループをすることができるそうだ。スゲぇー

着手してみた

早速コードを書いて、usersの各値にpathを埋め込んでいきましょう。

// 上記のJSONを変数で定義します。。
const users = [
  { name: "タカシ",
    children: [
      { name: "ミツル" },
      { name: "ナツキ",
        children: [
          { name: "アイナ" },
          { name: "ナオキ" }
        ]
      },
      { name: "ヨツハ" },
      { name: "フタバ",
        children: [
          { name: "リク" }
        ]
      }
    ]
  },
  { name: "ソラ",
    children: [
      { name: "ハナ" }
    ]
  }
]

// 関数funcを定義
const func = (users, path=[]) => {
  users.forEach((user, i) => {

    // userにpathを追加
    user.path = [...path, i];

    if (user.children) {
      // user.childrenがあれば関数を再び呼び出す
      func(user.children, user.path);
    }
  });
};

func(users);
console.log(users);

// 結果
// [
//   { name: "タカシ",
//     path: [ 0 ],
//     children: [
//       {
//         name: "ミツル",
//         path: [ 0, 0 ]
//       },
//       { name: "ナツキ",
//         path: [ 0, 1 ],
//         children: [
//           {
//             name: "アイナ",
//             path: [ 0, 1, 0 ],
//           },
//           {
//             name: "ナオキ",
//             path: [ 0, 1, 1 ],
//           }
//         ]
//       },
//       {
//         name: "ヨツハ",
//         path: [ 0, 2 ],
//       },
//       {
//         name: "フタバ",
//         path: [ 0, 3 ],
//         children: [
//           {
//             name: "リク",
//             path: [ 0, 3, 0 ]
//           }
//         ]
//       }
//     ]
//   },
//   {
//     name: "ソラ",
//     path: [ 1 ],
//     children: [
//       {
//         name: "ハナ",
//         path: [ 1, 0 ]
//       }
//     ]
//   }
// ]

※図解
タカシ, path: [ 0 ]
┗ ミツル, path: [ 0, 0 ]
┗ ナツキ, path: [ 0, 1 ]
    ┗ アイナ, path: [ 0, 1, 0 ]
    ┗ ナオキ, path: [ 0, 1, 0 ]
┗ ヨツハ, path: [ 0, 2 ]
┗ フタバ, path: [ 0, 3 ]
    ┗ リク, path: [ 0, 3, 0 ]
ソラ, path: [ 1 ]
┗ ハナ, path: [ 1, 0 ]

引数にusersの配列とそれまでのpathを渡して各userごとにpathの埋め込んでいくという作業を再帰的に行なっています。
pathに関してはforEachのindexをaddする形で使っています。

user.path = [...path, i];は引数pathを スプレッド構文 を利用して展開して利用しています。
スプレッド構文はシンタックスシュガーです。

  // スプレッド構文を用いない場合
  user.path = path;
  user.path.push(i);


  // スプレッド構文を用いた場合
  user.path = [...path, i];

この二つは同じ結果になります。詳しくは、@Nossaさんがまとめてくれている記事がありますのでそちらをご覧ください。

最後に

再帰関数をある程度利用することでjsを扱う上での視野が広がった気がします。
また、記事に間違いや不明な点があれば遠慮なくご指摘いただけますと幸いです‎✧。٩(ˊωˋ)و✧*。

明日は、@kuwakuwakuwaさん担当です!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away