У меня есть многомерный массив, содержащий некоторые идентификаторы, хранящиеся в ключах под названием «имя». Каждая запись может иметь другие поддиапазоны, содержащие другие идентификаторы. Массив динамический; глубина и записи неизвестны. Вот пример:
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. Каждый уровень глубже означает, что запись является дочерним элементом родительского уровня. Поэтому я должен хранить каждый предыдущий уровень глубины «глубина» при цикле массива. До сих пор я не нашел способ сделать это.
ОБНОВЛЕНИЕ ВОПРОСА:
У меня первая работа. Теперь я хочу добавить новые главы в массив и обновить номера глав существующих записей. Теперь массив выглядит так:
Array ( [1] => test1 [1.1] => test2 [1.2] => test3 [1.2.1] => test4 [2] => test5 )
Итак, теперь я хотел бы добавить главу 'test6' как first-child из 1.2, что означает, что текущий 1.2.1 станет 1.2.2, а новый ребенок будет 1.2.1.
Код:
// 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 )
демонстрация
РЕДАКТИРОВАТЬ
Эти две (плюс одна поддержка) функций позволяют добавлять и удалять главы из входного массива по ссылке главы. Затем вы можете пересчитать 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 ) */
Теперь предположим, что мы хотим добавить главу под названием test6
качестве главы 1.1
, а test2
будет переиндексирована на 1.2
– мы будем работать с результатом приведенного выше примера для этого:
// 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 ) */
Хорошо, кажется довольно простым. Но что, если мы хотим переместить подглаву, так что это было на верхнем уровне дерева? Вернемся к нашей первоначальной версии $structure
чтобы продемонстрировать это – мы перейдем к главе 1.2
, так что теперь это глава 3
:
/* 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
в качестве первого параметра и идентификатор раздела, который вы хотите проверить как второй. Эта функция требуется, так как она используется другими двумя внутренне.
add_chapter()
возвращает логическое значение, поэтому вы можете проверить, была ли операция успешной. Он не сработает, если родитель главы не существует – например, если вы попытаетесь добавить 1.2.1
когда 1.2
не определено, это не сработает. Если вы добавите главу, которая уже существует, все номера глав на этом уровне будут сдвинуты на 1.
remove_chapter()
вернет элемент, который был удален при успешном завершении (т. е. массив) или логический FALSE
при сбое – он потерпит неудачу, если вы попытаетесь удалить раздел, который не существует.
NB: Мне пришлось сильно использовать eval()
для этого, чтобы учесть произвольную глубину уровня. Я ненавижу использовать его, но я не мог думать ни о каком другом способе – если кто-то читает это, есть какие-то яркие идеи об альтернативных подходах (желательно, чтобы они не включали какую-то кошмарную петлевую структуру), пожалуйста, дайте мне знать …
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));
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; }