2011-10-25 4 views
1

私はいくつかのIDを含む多次元配列を持っています、 '名前'と呼ばれるキーに格納されています。各エントリは、他のIDを含む他のサブ配列を持つことができます。配列は動的です。深さとエントリは不明です。PHP深さとキーを保持しながら多次元配列を再帰

Array 
(
    [0] => Array 
     (
      [name] => test1 
      [subs] => Array 
       (
        [0] => Array 
         (
          [name] => test2 
         ) 

        [1] => Array 
         (
          [name] => test3 
          [subs] => Array 
            (
             [name] => test4 
            ) 
         ) 

       ) 

     ) 

    [1] => Array 
     (
      [name] => test5 
     ) 
) 

は、今私は深さのホールドを維持しながら、「フラット」のアレイに、この多次元配列を変換したい:ここでは一例です。新しい配列のスコープは、キーが章を表し、値がidである何らかの種類の目次です。たとえば、 'test4'は1.2.1章、 'test2'は1.1、 'test5'は第2章である必要があります。各レベルは、エントリが親レベルの子であることを意味します。したがって、配列をループしながら、以前のすべての深さ 'レベル'を保存する必要があります。これまで私はこれを行う方法を見つけていない。

QUESTIONのUPDATE:

私は、最初の部分の作業を持っています。今度は、新しいチャプタを配列に追加し、既存のエントリの章番号を更新したいと考えています。だから今、私は現在の1.2.1は1.2.2になるだろうと新しい子がなることを意味しており、1.2の最初の子として章「TEST6」を追加したいと思い

Array 
(
    [1] => test1 
    [1.1] => test2 
    [1.2] => test3 
    [1.2.1] => test4 
    [2] => test5 
) 

:配列は次のようになります代わりに1.2.1。

+0

再帰関数:) –

+2

私はあなたの質問に顕著な違いを参照いけない:http://stackoverflow.com/questions/7854940/php-walk-through-multidimensional-array-while-preserving-keysを - 私は再び同じ可能な重複をお勧めします。 – Gordon

答えて

3

コード:

// Mmmm... functiony goodness 
function array_to_toc ($in, &$out, $level = '') { 
    if (!$level) $out = array(); // Make sure $out is an empty array at the beginning 
    foreach ($in as $key => $item) { // Loop items 
    $thisLevel = ($level) ? "$level.".($key + 1) : ($key + 1); // Get this level as string 
    $out[$thisLevel] = $item['name']; // Add this item to $out 
    if (isset($item['subs']) && is_array($item['subs']) && count($item['subs'])) array_to_toc($item['subs'],$out,$thisLevel); // Recurse children of this item 
    } 
} 

// Here is your test data (slightly modified - I think you stated it wrong in the question) 
$array = array (
    0 => array (
    'name' => 'test1', 
    'subs' => array (
     0 => array (
     'name' => 'test2' 
    ), 
     1 => array (
     'name' => 'test3', 
     'subs' => array (
      0 => array (
      'name' => 'test4' 
     ) 
     ) 
    ) 
    ) 
), 
    1 => array (
    'name' => 'test5' 
) 
); 

// $result is passed by reference and will hold the output after the function has run 
$result = array(); 
array_to_toc($array, $result); 

print_r($result); 

出力:

Array 
(
    [1] => test1 
    [1.1] => test2 
    [1.2] => test3 
    [1.2.1] => test4 
    [2] => test5 
) 

Demo

EDIT

これら2つ(プラスワン支援)機能は、あなたからの章を追加および削除が可能aを入力章の参照によるrray。次に、新しい構造からTOCを再計算することができます。

function chapter_exists ($array, $chapterId) { 
    $chapterParts = explode('.',$chapterId); 
    foreach ($chapterParts as &$chapter) $chapter--; 
    $lastId = array_pop($chapterParts); 
    return eval('return isset($array['.implode("]['subs'][",$chapterParts).((count($chapterParts)) ? "]['subs'][" : '')."$lastId]);"); 
} 

function add_chapter (&$array, $chapterId, $item) { 
    $chapterParts = explode('.',$chapterId); 
    foreach ($chapterParts as &$chapter) $chapter--; // Decrement all the values 
    $lastId = array_pop($chapterParts); 
    if (count($chapterParts) && !chapter_exists($array, implode('.',$chapterParts))) return FALSE; // Return FALSE if the level above the chapter we are adding doesn't exist 
    if (chapter_exists($array, $chapterId)) { // See if the chapter reference already exists 
    eval('array_splice($array'.((count($chapterParts)) ? '['.implode("]['subs'][",$chapterParts)."]['subs']" : '').",$lastId,0,array(\$item));"); // Insert an item 
    } else { 
    eval('$array['.implode("]['subs'][",$chapterParts).((count($chapterParts)) ? "]['subs'][" : '')."$lastId] = \$item;"); // Insert an item 
    } 
    return TRUE; 
} 

function remove_chapter (&$array, $chapterId) { 
    $chapterParts = explode('.',$chapterId); 
    foreach ($chapterParts as &$chapter) $chapter--; // Decrement all the values 
    $lastId = array_pop($chapterParts); 
    return (chapter_exists($array, $chapterId)) ? eval('$removed = array_splice($array'.((count($chapterParts)) ? '['.implode("]['subs'][",$chapterParts)."]['subs']" : '').",$lastId,1); return array_shift(\$removed);") : FALSE; 
} 

どのように動作するかを実証する最も良い方法は、例です。上の配列構造から始めて、$structureという変数に格納されているとします。私たちが知っているように、私たちの結果のTOCの配列は次のようになります。

Array 
(
    [1] => test1 
    [1.1] => test2 
    [1.2] => test3 
    [1.2.1] => test4 
    [2] => test5 
) 

を今は、我々は章1.2を削除するかを決定し、すべてはそれがサブ章だ - 私たちはこれを行うことができます:

// Remove the chapter from $structure 
remove_chapter($structure, '1.2'); 
// recalculate the TOC 
array_to_toc($structure, $result2); 

print_r($result2); 
/* 
    Outputs: 
    Array 
    (
     [1] => test1 
     [1.1] => test2 
     [2] => test5 
) 
*/ 

今すぐことができます章1.1としてtest6と呼ばれ、test21.2に再インデックスされます、我々は章を追加したいと言う - 私たちは、この1のために、上記の例の結果で作業することがあります:

// Add the new chapter to $structure 
add_chapter($structure, '1.1', array('name'=>'test6')); 
// recalculate the TOC 
array_to_toc($structure, $result3); 

print_r($result3); 
/* 
    Outputs: 
    Array 
    (
     [1] => test1 
     [1.1] => test6 
     [1.2] => test2 
     [2] => test5 
) 
*/ 

OK、かなりシンプルなようです。しかし、我々がに移動したいのであれば、をサブチャプターに移動すると、それはツリーの最上位にありますか?のは、このことを実証する$structureの私達の元のバージョンに戻りましょう - それは今章3であるように、我々は、章1.2を移動します:

/* 
    A quick reminder of what we are starting with: 
    Array 
    (
     [1] => test1 
     [1.1] => test2 
     [1.2] => test3 
     [1.2.1] => test4 
     [2] => test5 
) 
*/ 

// Remove the chapter from $structure - this time, we'll catch the items we remove in a variable 
$removed = remove_chapter($structure, '1.2'); 
// Add it again, only this time as chapter 3 
add_chapter($structure, '3', $removed); 

// recalculate the TOC 
array_to_toc($structure, $result4); 

print_r($result4); 
/* 
    Outputs: 
    Array 
    (
     [1] => test1 
     [1.1] => test2 
     [2] => test5 
     [3] => test3 
     [3.1] => test4 
) 
*/ 

がうまくいけば、私はそこにそれが十分に説明してきました。

chapter_exists()はブール値を返します。感じるならば、それが意味するものについてかなり自明です。最初のパラメーターとして$structure配列を渡し、2番目のパラメーターとしてチェックするチャプターIDを渡します。他の2つの内部で使用されているので、この関数は必須です。

add_chapter()はブール値を返します。したがって、操作が成功したかどうかをテストできます。章の親が存在しない場合は失敗します。たとえば、1.2が定義されていない場合に1.2.1を追加しようとすると、機能しません。既に存在するチャプターを追加すると、そのレベルのすべてのチャプター番号が1つ上にシフトされます。

remove_chapter()は、成功すると削除されたアイテム(つまり配列)またはブール値FALSEを返します。存在しない章を削除しようとすると失敗します。

NB:これは、任意のレベルの深さに対応するために、eval()を多用しなければなりませんでした。私はそれを使用することを嫌っていますが、私は他の方法を考えることができませんでした - 誰かがこれを読んでいる人は、代替的なアプローチについての明るいアイデアを持っていれば、私に教えてください...

+0

ありがとう、これは私のために働く。今、私は既存のものの数を更新しながら新しい章を追加する方法を考えています。上記の私の更新を確認してください。 – carlo

+0

TOCをゼロから再構築するために、上記の関数に変更された入力配列を渡すだけでは問題ありませんか?これは、 '$ result'配列を直接変更しようとするよりも簡単です* – DaveRandom

+0

それはうまくいくかもしれませんが、その入力配列にどのように新しいエントリを挿入すればよいですか?申し訳ありませんが、多次元配列との親和性はほとんどありません。 – carlo

0
function toc(array $data, array $level = array()) { 
    $toc = array(); 

    foreach ($data as $i => $node) { 
     $currentLevel = array_merge($level, array($i + 1)); 
     $toc[] = join('.', $currentLevel) . ': ' . $node['name']; 
     if (!empty($node['subs'])) { 
      $toc = array_merge($toc, toc($node['subs'], $currentLevel)); 
     } 
    } 

    return $toc; 
} 

echo join("\n", toc($array)); 
0
function array_flat($array, $prefix = '') { 
$result = array(); 

foreach ($array as $key => $value) { 
    $new_key = $prefix . (empty($prefix) ? '' : '.') . $key; 

    if (is_array($value)) { 
     $result = array_merge($result, array_flat($value, $new_key)); 
    } else { 
     $result[$new_key] = $value; 
    } 
} 

return $result; 
} 
関連する問題