Kei05
@Kei05

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

平坦な配列内のオブジェクトを階層化したい

解決したいこと

flatArrnewArrのような階層構造にしたいのですが、うまく求めている結果になりません。
ご教示いただきたいです。

const flatArr = [
  { "element": "heading_1" },
  { "element": "heading_2" },
  { "element": "heading_3" },
  { "element": "heading_2" },
  { "element": "heading_1" },
  { "element": "heading_2" },
  { "element": "heading_2" }
]

const newArr = [
  {
    "element": "heading_1",
    "children": [
      {
        "element": "heading_2"
        "children": [{ "element": "heading_3" }]
      },
      {
        "element": "heading_2"
      }
    ]
  },
  {
    "element": "heading_1",
    "children": [
      { "element": "heading_2" }
      { "element": "heading_2" }
    ]
  }
]
0

2Answer

flatArrに登場するelementはh3までですか?
また、h1の次にh2を飛ばしてh3が入ることなどはありますか?

0Like

Comments

  1. @Kei05

    Questioner

    Notion APIとNext.jsを使ってブログを作ろうとしているので、入る見出し要素はh2〜h4になります。
    上の例でh1としているのはブロック名がheading_1〜heading_3で取得されているので少しわかりにくかったですね。。elementにはheading_1〜heading_3が入ると考えていただくのが求める回答に最も近いです。
    これに合わせて少し質問時のソースも修正しております。

    > h1の次にh2を飛ばしてh3が入ることなどはありますか?
    → 可能性としてはありますが、これに関しては運用でカバーできるので厳しければ飛ばないとしていただいても問題ございません。

こんな感じでどうでしょうか?
もっといい書き方もありそうですが、h1~h3までしかないのであれば愚直にやってもそんなに大した量にはならないので。

const flatArr = [
  { element: 'heading_1' },
  { element: 'heading_2' },
  { element: 'heading_3' },
  { element: 'heading_2' },
  { element: 'heading_1' },
  { element: 'heading_2' },
  { element: 'heading_2' },
];

const result = flatArr.reduce((prev, current) => {
  const h1LastIndex = prev.length - 1;
  switch (current.element) {
    case 'heading_1':
      prev.push(current);
      break;
    case 'heading_2':
      if (h1LastIndex < 0) {
        throw new Error('h2タグ順番エラー');
      }
      if (!prev[h1LastIndex].children) {
        prev[h1LastIndex].children = [];
      }
      prev[h1LastIndex].children.push(current);
      break;
    case 'heading_3':
      const h2LastIndex = prev[h1LastIndex].children.length - 1;
      if (h1LastIndex < 0 || h2LastIndex < 0) {
        throw new Error('h3タグ順番エラー');
      }
      if (!prev[h1LastIndex].children[h2LastIndex].children) {
        prev[h1LastIndex].children[h2LastIndex].children = [];
      }
      prev[h1LastIndex].children[h2LastIndex].children.push(current);
      break;
    default:
      break;
  }
  return prev;
}, []);
console.log(JSON.stringify(result, null, 2));

// 出力結果
[
  {
    "element": "heading_1",
    "children": [
      {
        "element": "heading_2",
        "children": [
          {
            "element": "heading_3"
          }
        ]
      },
      {
        "element": "heading_2"
      }
    ]
  },
  {
    "element": "heading_1",
    "children": [
      {
        "element": "heading_2"
      },
      {
        "element": "heading_2"
      }
    ]
  }
]

0Like

Comments

  1. @Kei05

    Questioner

    ありがとうございます!
    試してみましたが「const h1LastIndex = prev.length - 1」で下記のエラーとなってしまいました。
    > TypeError: Cannot read properties of undefined (reading 'length')
  2. そのままコピーして実行ですか?それとも何かしらの修正を加えて実行してエラーでしょうか?
    エラーの内容的にreduceの初期値がundefinedになっているように見えます。
  3. @Kei05

    Questioner

    失礼しました。
    current.elementを取得される値にあわせて若干修正が必要でした。
    無事、思い通りの動作ができました。ありがとうございます。

Your answer might help someone💌