Построить пути всех путей древовидного массива в Stack Overflow

Привет, я пытаюсь напечатать категории и их детей, как дерево путей из БД. До сих пор я могу распечатать древовидный массив, как показано ниже.

Array
(
[0] => Array
(
[name] => 'Furniture'
[slug] => 'furniture'
[children] => Array
(
[0] => Array
(
[name] => 'Sofa'
[slug] => 'sofa'
[leafs] => Array
(
[0] => Array ( [name] => '3 Seater', [slug] = '3-seater'
[1] => Array ( [name] => '4 Seater', [slug] = '4-seater'
)

)

[1] => Array
(
[name] => 'Chairs'
[slug] => 'chairs'
[leafs] => Array
(
[0] => Array ( [name] => '3 Seater', [slug] = '3-seater'
[1] => Array ( [name] => '4 Seater', [slug] = '4-seater'
)

)

)

)

[1] => Array
(
[name] => 'Furniture1'
[slug] => 'furniture1'
[children] => Array
(
[0] => Array
(
[name] => 'Sofa1'
[slug] => 'sofa1'
[leafs] => Array
(
[0] => Array ( [name] => '3 Seater1', [slug] = '3-seater1'
[1] => Array ( [name] => '4 Seater1', [slug] = '4-seater1'
)

)

[1] => Array
(
[name] => 'Chairs1'
[slug] => 'chairs1'
[leafs] => Array
(
[0] => Array ( [name] => '3 Seater1', [slug] = '3-seater1'
[1] => Array ( [name] => '4 Seater1', [slug] = '4-seater1'
)

)

)

)

)

У некоторых детей могут быть листья или у некоторых родителей могут быть дети. Но то, что я пытаюсь напечатать, было похоже ниже.

Array(
[0] => 'Furniture/Sofa/3 Seater',
[1] => 'Furniture/Sofa/4 Seater',
[2] => 'Furniture/Chiars/ 3 Seater'
[3] => 'Furniture/Chiars/4 Seater',
[4] => 'Furniture1/Sofa1/3 Seater1',
[5] => 'Furniture1/Sofa/4 Seater1',
[6] => 'Furniture1/Chiars1/ 3 Seater1'
[7] => 'Furniture1/Chiars1/4 Seater1',
);

-1

Решение

Это «многоходовое дерево».

Обновить: Это полная переделка кода как класса TreePaths

Оригинальный полный исходный код на Pastebin.comВыполнить на sandbox.onlinephpfunctions.com

Этот ответ описывает новый код.

Перемены:

  • Вход array node теперь нужно только children запись, а не «листья». Это делает ввод более последовательным.

  • Единственная запись в array node проверено этим кодом $node['children], Это позволяет вам иметь любые другие данные в узле, которые вы хотите, и вы можете обрабатывать их в callable любым способом, который вы пожелаете.

  • Обработка узла может быть заменена любым callable имеющий подпись:

    function(array $currentPath, /* boolean */ $isTopALeaf) {...

  • Если nodeProcessor (callable) возвращает значение, которое не является пустым, тогда оно будет добавлено к $allPaths массив.

Внешние источники и демонстрации

Веб-сайт:

Описание

Интересно то, что мы должны хранить «путь» к «конечным» узлам.
«Путь» кажется «особенным». Однако, представьте, что каждый раз, когда вы «рекурсируете» или «вкладываете» вниз на один уровень, вы записываете, где находитесь в «стеке».
Когда вы достигнете конца текущего списка, вы:

  • Записать путь через «стек» к «текущему конечному узлу»
  • Позвоните nodeProcessor
  • ‘unnest’ или ‘pop’ вершина стека.

Я использую «древовидную структуру» и имена, как указано.

В конце каждого пути nodeProcessor называется с the current path

Выходы:

  • «полный путь» к любому конкретному «листовому» узлу.
  • Все пути к каждому «листовому» узлу.

Поскольку это дерево, то требуется «рекурсивный» алгоритм, поскольку «глубина» дерева неизвестна.

Программа должна:

  • пройти через «дерево».
  • поддерживать «стек» имен узлов как «путь» к текущему узлу
  • запускать пользовательский код на выбранных узлах по мере необходимости. Я использую «замыкание» для этого в этом коде.
  • хранить вывод где-нибудь полезным.

Node Processor (вызываемый) — замените по умолчанию

/*
* Generate an HTML anchor tag from the 'slug' entries
*/
$slugNodeProcessor =
function(array $currentPath,
$isTopALeaf)  {

/*
* Lets make some HTML anchors for the Slugs?
*/
$template = '<a href="%s" class="Slug">%s</a>';

// top of the stack will be a leaf
$title = '';
$top  = end($currentPath); // easier to work with
$prev = prev($currentPath); // need the description for title
$title = $prev['name'] .' - '.  $top['name'];

$url = '/';
foreach($currentPath as $key => $node) {
$url .= $node['Slug'] .'/';
};
$url = rtrim($url, '/');
$htmlSlug = sprintf($template, $url, $title);

return $htmlSlug;
};

По умолчанию ‘nodeProcessor’:

/**
* If you don't provide a callable to generate paths then this will be used.
*
* It generates a string of names separated by '/'. i.e. it looks like a filepath.
*
* @return string
*/
public function defaultNodeProcessor()
{
$dnp = function(array $currentPath,
$isTopALeaf)  {

$fullPath = '/';
foreach($currentPath as $key => $node) {
$fullPath .= $node['name'] .'/';
}
return rtrim($fullPath, '/');
};
return $dnp;
}

Запустите программу:

$tree = new TreePaths($srcTree,
$slugNodeProcessor);
$tree->generate();
$allPaths = $tree->allPaths();

Выход:

array (8) [
string (67) "<a href="/furniture/sofa/3-seater" class="Slug">Sofa - 3 Seater</a>"string (67) "<a href="/furniture/sofa/4-seater" class="Slug">Sofa - 4 Seater</a>"string (76) "<a href="/furniture/sofa/chairs/3-seater" class="Slug">Chairs - 3 Seater</a>"string (76) "<a href="/furniture/sofa/chairs/4-seater" class="Slug">Chairs - 4 Seater</a>"string (94) "<a href="/furniture/sofa/chairs/furniture1/sofa1/3-seater1" class="Slug">Sofa1 - 3 Seater1</a>"string (94) "<a href="/furniture/sofa/chairs/furniture1/sofa1/4-seater1" class="Slug">Sofa1 - 4 Seater1</a>"string (104) "<a href="/furniture/sofa/chairs/furniture1/sofa1/chairs1/3-seater1" class="Slug">Chairs1 - 3 Seater1</a>"string (104) "<a href="/furniture/sofa/chairs/furniture1/sofa1/chairs1/4-seater1" class="Slug">Chairs1 - 4 Seater1</a>"]

Класс:

/*
* Source Tree:
*
* Tree Node:
*
* Array(
*        "name" => 'Furniture',  // not checked
*        "slug" => 'furniture',  // optional - not used
*        "children" => Array( // will be other Tree nodes...
*                          ),
*      );
*
*  The `children` key is optional, if empty or missing, means it is a `leaf` node
*
*  !!! Note: The only array entry checked in here is 'children' !!!
*
*  But you will need to overide the default nodeProcessor.
*
*  The default `nodeProcessor` uses `name` and `children` only
*/

/*
* NodeProcessor:
*   o It is a callable that accepts two parameters
*     o current path - an array of all the nodes so far in this path
*     o isTopALeaf   - is the end of the path a 'leaf' node?
*/

/**
*  Traverse the tree of `nodes`
*  Generate a list of Paths from Root to every leaf node as an array of `nodes`.
*  It is a `stack` with the top node being a leaf.
*/
class TreePaths {

/**
* The 'current' menu / tree
*
* @var array $tree
*/
private $tree = array();


/**
* The Output
*
* @var array $allPaths
*/
private $allPaths = array();

/**
* The 'current' stack of nodes in this path
*
* This is a 'stack'. The 'path' is all the entries combined.
*
* @var array $currentPath
*/
private $currentPath = array();


/**
* The 'callable' to be run for nodes
*
* @var callable $nodeProcessor
*/
private $nodeProcessor = null;


/**
* Call All Nodes or Leaf node only
*
* @var boolean
*/
private $callLeafNodesOnly = true;


/**
* Build the class but do not run it...
*
* Provides a default NodeProcessor if you don't provide one.
*    o The default processor builds string paths that look like filepaths
*
* @param array $tree
* @param callable $processNode        - optional
* @param boolean $callLeafNodesOnly  - optional default true
*/
public function __construct(array $tree,
/* callable */ $processNode = null,
$callLeafNodesOnly = true)
{
$this->tree = $tree;
$this->nodeProcessor = $processNode;
$this->callLeafNodesOnly = $callLeafNodesOnly;

// provide a default processor
if (is_null($this->nodeProcessor)) {
$this->nodeProcessor = $this->defaultNodeProcessor();
}
}

/**
* This routine makes this class rather powerful as you can use any callable.
*
* @param type $nodeProcessor
*/
public function setNodeProcessor(/* callable */ $nodeProcessor)
{
$this->nodeProcessor = $nodeProcessor;
}

/**
* Return a list of all the paths that were generated by the 'nodeProcessor'
* @return array
*/
public function allPaths()
{
return $this->allPaths;
}

/**
* The routine that processes one node and recurses as required
*
* @param array $node
* @return void This is all side-effects
*/
protected function treeWalk($node)
{
// always add the node to the currentPath
array_push($this->currentPath, $node);

// Always call the node processor and add the path to all paths if required
$processedPath = $this->callNodeProcessor($this->currentPath,
$this->isLeafNode($node));
if (!empty($processedPath)) { // add to all the paths
$this->allPaths[] = $processedPath;
}

// do we recurse?
if ($this->isLeafNode($node)) { // no we dont...
array_pop($this->currentPath); // lose leaf node from top of stack
return; // nothing more to do
}

// now process all the children... This will recurse - always
foreach ($node['children'] as $key => $node) {
$this->treeWalk($node);
}
return; // end of children
}

/**
* Process all the top level nodes.
*
* @return void
*/
public function generate()
{
$this->allPaths = array();

foreach ($this->tree as $key => $node) {
$this->treeWalk($node);
}
return;
}

/**
* End of a path?
*
* @param array $node
* @return boolean
*/
protected function isLeafNode($node)
{
return empty($node['children']);
}

/**
* Are we in the 'middle' of a path?
*
* @param array $node
* @return boolean
*/
protected function hasChildren($node)
{
return !empty($node['children']);
}


/**
* The `callable` to be called.
*
* It must accept the two parameters.
*
* It can be set after the 'class instance' is created.
*
* @param array currentPath to this value
* @param string nodeType - leaf or children
*
* @return mixed if not empty will be added to the paths
*/
protected function callNodeProcessor(array $currentPath,
$isTopALeaf)
{

if ($this->callLeafNodesOnly) {
if ($isTopALeaf)  {
return call_user_func($this->nodeProcessor,
$currentPath,
$isTopALeaf);
}
}
else {
return call_user_func($this->nodeProcessor,
$currentPath,
$isTopALeaf);
}
}

/**
* If you don't provide a callable to generate paths then this will be used.
*
* It generates a string of names separated by '/'. i.e. it looks like a filepath.
*
* @return string
*/
public function defaultNodeProcessor()
{
$dnp = function(array $currentPath,
$isTopALeaf)  {

$fullPath = '/';
foreach($currentPath as $key => $node) {
$fullPath .= $node['name'] .'/';
}
return rtrim($fullPath, '/');
};
return $dnp;
}
}

Формат исходных данных:

/*
* Tree Node:
*
* Array(
*        "name" => 'Furniture',
*        "slug" => 'furniture',
*        "children" => Array( // can be other Tree nodes...
*                          ),
*      );
*
*  The `children` key is optional, if empty or missing, means it is a `leaf` node
*
*  !!! Note: The only array entry checked in here is 'children' !!!
*
*  But you would need to overide the default nodeProcessor.
*/

Образцы тестовых данных

$srcTree = Array(
0 => Array(
"name" => 'Furniture',
"Slug" => 'furniture',
"children" => Array(
"0" => Array
(
"name" => 'Sofa',
"Slug" => 'sofa',
"children" => Array
(
"0" => Array ( "name" => '3 Seater', "Slug" => '3-seater'),
"1" => Array ( "name" => '4 Seater', "Slug" => '4-seater'),
),

),

"1" => Array
(
"name" => 'Chairs',
"Slug" => 'chairs',
"children" => Array
(
"0" => Array ( "name" => '3 Seater', "Slug" => '3-seater'),
"1" => Array ( "name" => '4 Seater', "Slug" => '4-seater'),
)

)

)

),
More entries here ...
2

Другие решения

$fullname = array();
$mainarrs = array(); //this is the array that will have your all the array data
$i=0;
foreach ( $mainarrs as $mainarr )
{

$temp = $mainarr['name'];

foreach($mainarr as $anmain)
{
$temp2 = $anmain['name'];

foreach($anmain as $lastmain)
{
$fullname[$i] = $temp."/".$temp2."/".$lastmain['name']
}
}
}

//now for the output that you expect to see is in $fullname array, you can loop through it to see the result.

Я надеюсь, это поможет вам.

0

По вопросам рекламы [email protected]