QA@IT
«回答へ戻る

回答を投稿

もう少し {"text":"9","level":"2"} などで出てくる text (ファイル名・ディレクトリ名?)、 level , mlevel(階層?) などの説明が欲しかったですね。

「逆」というのはつまり、このファイル名/ディレクトリ名と深さの配列からツリーを再構築したいということですよね。

[ {"text":"1","level":"1"},
{"text":"2","level":"2"},
〜 略 〜
{"text":"9","level":"2"},
{"text":"10","level":"3"}];

各要素は名前と深さしか持っていませんが、よくあるデータの持ち方としては、
これに加えて親の情報と、ディレクトリかファイルかの情報を持たせたほうが処理はしやすくなると思います。

これらがない場合は、データ構造を取得処理(この配列の元データを作った処理)に依存し、さらに確定できない情報もあると思います。

現在取得するときに再帰で(おそらく並列処理せずに)取っているとのことですが、
例えばディレクトリ一覧の取得処理が

  1. 渡したディレクトリのすべてのディレクトリ・ファイルについて処理する
  2. ディレクトリからファイルか判断
    1. ディレクトリであれば出力し、すぐに自身を再帰的に呼び出す。
    2. ファイルであれば出力する。

ここから構造を推測するとします。

この再帰処理には
「順に見ていってディレクトリはすぐ再帰して子供を取りに行く」という特性があるので、
levelが大きくなったら(サブディレクトリの情報に移ったら)、その前のアイテムが親ディレクトリです。(親「ディレクトリ」なのでこの前のアイテムはディレクトリと確定できます。)

また、ディレクトリの情報も残しているので、levelが増加するときは常に1ずつとなります。

[ {"text":"1","level":"1"},    // 次の行でlevelが増えているので "text":"1" はディレクトリ
    {"text":"2","level":"2"},
    {"text":"3","level":"2"},  // 次の行でlevelが増えているので "text":"3" はディレクトリ
    {"text":"4","level":"3"},  // "text":"4" はディレクトリ
    {"text":"5","level":"4"},  // "text":"5" はディレクトリ
    {"text":"6","level":"5"},

逆にlevelが減った場合は下の階層を探索し尽くして親ディレクトリに戻り、そのlevelの続きのアイテムだと意味します。

    {"text":"2","level":"2"},
    {"text":"3","level":"2"},
    {"text":"4","level":"3"},
    {"text":"5","level":"4"},
    {"text":"6","level":"5"},  
    {"text":"7","level":"4"},  // level 4 の続き ( "text":"5" の兄弟 )
    {"text":"8","level":"5"},
    {"text":"9","level":"2"},  // level 2 の続き ( "text":"3" の兄弟 )
    {"text":"10","level":"3"}];

というような処理をjavascriptで書くと以下のようになります。
(phpで書く時間がなかったのですいません)
type という属性も勝手に追加しています。

子要素が出てきたら再帰処理で子要素の配列を作成します。

<!DOCTYPE html>
<html><head lang="ja"></head>
<body>
<textarea name="result" id="result" cols="40" rows="10"></textarea>
<script>
var items = [ {"text":"1","level":"1"},
{"text":"2","level":"2"},
{"text":"3","level":"2"},
{"text":"4","level":"3"},
{"text":"5","level":"4"},
{"text":"6","level":"5"},
{"text":"7","level":"4"},
{"text":"8","level":"5"},
{"text":"9","level":"2"},
{"text":"10","level":"3"}];

var result = [];


/**
 * 
 * @param result 見つかったアイテムを追加していく配列。処理後にここに結果が格納されている。
 * @param items 元情報となる配列
 * @param index itemsの検索開始インデックス(再帰処理で途中から探索するために必要)
 * @param currentLevel 探索対象のlevel
 * @returns {*} 最後に探索されたindex。再帰から戻ったときにこのindexから続きを行う
 */
 function rebuildTree(result, items, index, currentLevel){

    var i = index; // 受け取ったインデックスからループする

    while(i< items.length){
        if(currentLevel != items[i].level * 1 ) {

            if( currentLevel > items[i].level * 1 ){
                // 下の階層の探索が終わって元の階層に戻っているところ
                return i;
            }

            if( items[i].level * 1 == currentLevel + 1 ) {

                // 下の階層のデータが出てきたので 再帰で下のディレクトリの情報を再構築
                var children = [];
                i = rebuildTree(children, items, i, items[i].level * 1)
                result[result.length - 1].children = children;

                // 子がいたので、ディレクトリに確定する
                result[result.length - 1].type = "directory"
            }

        }else{
            result.push({ text: items[i].text, level: items[i].level, type: "dir or file", children: [] });
            currentLevel = items[i].level * 1;
            i++;
        }

    }
    return i;
}

rebuildTree(result, items, 0, 1);

document.getElementById("result").value = JSON.stringify(result);
console.log(JSON.stringify(result));
</script>
</body></html>

ただし、この処理は、空のディレクトリとファイルの区別はつきません。
再帰処理の構成が私の想定と違う場合も処理を修正しないといけません。

親ディレクトリ情報は追加した方が良いと思います。
(この場合は再構築の方法は違うやり方も出てくると思います。)

もう少し  `{"text":"9","level":"2"}` などで出てくる text (ファイル名・ディレクトリ名?)、 level , mlevel(階層?) などの説明が欲しかったですね。

「逆」というのはつまり、このファイル名/ディレクトリ名と深さの配列からツリーを再構築したいということですよね。
```
[ {"text":"1","level":"1"},
{"text":"2","level":"2"},
〜 略 〜
{"text":"9","level":"2"},
{"text":"10","level":"3"}];
```

各要素は名前と深さしか持っていませんが、よくあるデータの持ち方としては、
これに加えて親の情報と、ディレクトリかファイルかの情報を持たせたほうが処理はしやすくなると思います。

これらがない場合は、データ構造を取得処理(この配列の元データを作った処理)に依存し、さらに確定できない情報もあると思います。

現在取得するときに再帰で(おそらく並列処理せずに)取っているとのことですが、
例えばディレクトリ一覧の取得処理が
1. 渡したディレクトリのすべてのディレクトリ・ファイルについて処理する
2. ディレクトリからファイルか判断
    1. ディレクトリであれば出力し、すぐに自身を再帰的に呼び出す。
    2. ファイルであれば出力する。

ここから構造を推測するとします。

この再帰処理には
「順に見ていってディレクトリはすぐ再帰して子供を取りに行く」という特性があるので、
levelが大きくなったら(サブディレクトリの情報に移ったら)、その前のアイテムが親ディレクトリです。(親「ディレクトリ」なのでこの前のアイテムはディレクトリと確定できます。)

また、ディレクトリの情報も残しているので、levelが増加するときは常に1ずつとなります。

```
[ {"text":"1","level":"1"},    // 次の行でlevelが増えているので "text":"1" はディレクトリ
    {"text":"2","level":"2"},
    {"text":"3","level":"2"},  // 次の行でlevelが増えているので "text":"3" はディレクトリ
    {"text":"4","level":"3"},  // "text":"4" はディレクトリ
    {"text":"5","level":"4"},  // "text":"5" はディレクトリ
    {"text":"6","level":"5"},
```

逆にlevelが減った場合は下の階層を探索し尽くして親ディレクトリに戻り、そのlevelの続きのアイテムだと意味します。

```
    {"text":"2","level":"2"},
    {"text":"3","level":"2"},
    {"text":"4","level":"3"},
    {"text":"5","level":"4"},
    {"text":"6","level":"5"},  
    {"text":"7","level":"4"},  // level 4 の続き ( "text":"5" の兄弟 )
    {"text":"8","level":"5"},
    {"text":"9","level":"2"},  // level 2 の続き ( "text":"3" の兄弟 )
    {"text":"10","level":"3"}];
```


というような処理をjavascriptで書くと以下のようになります。
(phpで書く時間がなかったのですいません)
type という属性も勝手に追加しています。

子要素が出てきたら再帰処理で子要素の配列を作成します。

```html
<!DOCTYPE html>
<html><head lang="ja"></head>
<body>
<textarea name="result" id="result" cols="40" rows="10"></textarea>
<script>
var items = [ {"text":"1","level":"1"},
{"text":"2","level":"2"},
{"text":"3","level":"2"},
{"text":"4","level":"3"},
{"text":"5","level":"4"},
{"text":"6","level":"5"},
{"text":"7","level":"4"},
{"text":"8","level":"5"},
{"text":"9","level":"2"},
{"text":"10","level":"3"}];

var result = [];


/**
 * 
 * @param result 見つかったアイテムを追加していく配列。処理後にここに結果が格納されている。
 * @param items 元情報となる配列
 * @param index itemsの検索開始インデックス(再帰処理で途中から探索するために必要)
 * @param currentLevel 探索対象のlevel
 * @returns {*} 最後に探索されたindex。再帰から戻ったときにこのindexから続きを行う
 */
 function rebuildTree(result, items, index, currentLevel){

    var i = index; // 受け取ったインデックスからループする

    while(i< items.length){
        if(currentLevel != items[i].level * 1 ) {

            if( currentLevel > items[i].level * 1 ){
                // 下の階層の探索が終わって元の階層に戻っているところ
                return i;
            }

            if( items[i].level * 1 == currentLevel + 1 ) {

                // 下の階層のデータが出てきたので 再帰で下のディレクトリの情報を再構築
                var children = [];
                i = rebuildTree(children, items, i, items[i].level * 1)
                result[result.length - 1].children = children;

                // 子がいたので、ディレクトリに確定する
                result[result.length - 1].type = "directory"
            }

        }else{
            result.push({ text: items[i].text, level: items[i].level, type: "dir or file", children: [] });
            currentLevel = items[i].level * 1;
            i++;
        }

    }
    return i;
}

rebuildTree(result, items, 0, 1);

document.getElementById("result").value = JSON.stringify(result);
console.log(JSON.stringify(result));
</script>
</body></html>
```

ただし、この処理は、空のディレクトリとファイルの区別はつきません。
再帰処理の構成が私の想定と違う場合も処理を修正しないといけません。

親ディレクトリ情報は追加した方が良いと思います。
(この場合は再構築の方法は違うやり方も出てくると思います。)