JS(TypeScript) 連想配列　検索処理について


**やりたいこと**  
・JS(TypeScript)の連想配列において、階層を取得し、特定階層以上はfalseにしたい。

[code]

    [
      {
        "id": 1,
        "name": "フォルダA",
        folder:[
        {
          "id":2,
          "name":"フォルダA-2"
        },
        {
          "id":3,
          "name":"フォルダA-3"
        }]
      },
      {
        "id": 4,
        "name": "フォルダB"
      },
      {
        "id": 5,
        "name": "フォルダC",
        folder:[
        {
          "id":6,
          "name":"フォルダC-1",
          folder:[
          {
            "id": 7,
            "name": "フォルダC-1-1"
          }]
        }]
      }
    ]
    
[/code]

上記のような入れ子構造のデータがあります。  
また、folderプロパティは必須ではない・idは一意です。

特定のidの階層を数え、特定の階層以上だった場合はfalseを返すメソッドを作成したいのですが、思い付かず悩んでいます。（再帰処理？）  
ご教授頂けないでしょうか。

例：id=6 の場合は、countNode=1としたい。id=7の場合は、countNode=2としたい。(dataは上記のデータ全ての想定)  
countNode>=10の場合はfalse

[code]

    const countNode(currentId:string,data:any):boolean => {
      const node = 0;
      const maxNode = 10;
      //以下処理悩み中。
    }
    
[/code]




参考: <https://repl.it/repls/CyanWiseQueries>

TSということなので、まずは対象となるデータ配列の要素をモデル化しておくと、次のように書けます:

[code]

    interface Data {
      id: number
      name: string
      folder?: Data[]
    }
    
[/code]

このような要素をもつデータ配列において、ある`id`をもつ要素の“深さ”は、ご指摘のとおり再帰処理で簡単に取得できます:

[code]

    const scanDepth = (data: Data[], id: number, depth: number = 0): number => {
      for (const d of data) {
        if (d.id === id) return depth
        if (d.folder) {
          const ret = scanDepth(d.folder, id, depth + 1)
          if (ret !== -1) return ret
        }
      }
      return -1
    }
    
    // for example
    console.assert(scanDepth(DATA, 1) === 0)
    console.assert(scanDepth(DATA, 6) === 1)
    console.assert(scanDepth(DATA, 7) === 2)
    
[/code]

あとは、`scanDepth`関数の返り値を判定すればよいでしょう（こまかい条件は必要に応じて直してください）:

[code]

    const verifyDepth = (data: Data[], id: number, maxdepth: number): boolean => scanDepth(data, id) < maxdepth
    
    // for example
    console.assert(verifyDepth(DATA, 1, 1))
    console.assert(verifyDepth(DATA, 6, 2))
    console.assert(verifyDepth(DATA, 7, 3))
    console.assert(!verifyDepth(DATA, 7, 2))
    console.assert(!verifyDepth(DATA, 7, 1))
    console.assert(!verifyDepth(DATA, 7, 0))
    
[/code]

ただし、`scanDepth`はナイーブな実装なので、必要以上に深く探索をするかもしれません。データ構造がかなり深くなることが事前に予測できるならば、探索を打ち切るなど改良の余地はあると思います。




参考: <https://repl.it/repls/CyanWiseQueries>

TSということなので、まずは対象となるデータ配列の要素をモデル化しておくと、次のように書けます:

[code]

    interface Data {
      id: number
      name: string
      folder?: Data[]
    }
    
[/code]

このような要素をもつデータ配列において、ある`id`をもつ要素の“深さ”は、ご指摘のとおり再帰処理で簡単に取得できます:

[code]

    const scanDepth = (data: Data[], id: number, depth: number = 0): number => {
      for (const d of data) {
        if (d.id === id) return depth
        if (d.folder) {
          const ret = scanDepth(d.folder, id, depth + 1)
          if (ret !== -1) return ret
        }
      }
      return -1
    }
    
    // for example
    console.assert(scanDepth(DATA, 1) === 0)
    console.assert(scanDepth(DATA, 6) === 1)
    console.assert(scanDepth(DATA, 7) === 2)
    
[/code]

あとは、`scanDepth`関数の返り値を判定すればよいでしょう（こまかい条件は必要に応じて直してください）:

[code]

    const verifyDepth = (data: Data[], id: number, maxdepth: number): boolean => scanDepth(data, id) < maxdepth
    
    // for example
    console.assert(verifyDepth(DATA, 1, 1))
    console.assert(verifyDepth(DATA, 6, 2))
    console.assert(verifyDepth(DATA, 7, 3))
    console.assert(!verifyDepth(DATA, 7, 2))
    console.assert(!verifyDepth(DATA, 7, 1))
    console.assert(!verifyDepth(DATA, 7, 0))
    
[/code]

ただし、`scanDepth`はナイーブな実装なので、必要以上に深く探索をするかもしれません。データ構造がかなり深くなることが事前に予測できるならば、探索を打ち切るなど改良の余地はあると思います。

