Как создать список, используя рекурсивную функцию для обработки вложенного массива?

Как можно реализовать рекурсивную функцию, которая на основе массива (с возможной неограниченной вложенностью) формирует список, используя только один аргумент и выводя тег ul только один раз в начале и в конце? Код должен работать без использования функций вроде str_replace и других подобных. Вот пример массива: 

```php
$arr = [
    'id' => 1,
    'name' => 'item1',
    'items' => [
        ['id' => 2,
        'name' => 'item2',
        'items' => [],
        ],
        ['id' => 3,
        'name' => 'item3',
        'items' => [],
        ],
        [
        'id' => 4,
        'name' => 'item4',
        'items' => [
            ['id' => 5,
            'name' => 'item5',
            'items' => [],
            ],
            ['id' => 6,
            'name' => 'item6',
            'items' => [],
            ]
        ]
        ]
    ]
];
```

Какой код у меня сейчас: 

```php
function list_form(array $array) {
    $str = '';
    foreach ($array as $key => $val) {
        if (is_array($val)) {
            $str .= "<li>$key =></li>";
            
            if (!empty($val)) {
                $str .= list_form($val);
            }
        }
        else {
            $str .= "<li>$key => $val</li>";
        }
    }
    return "<ul>" . str_replace(["<ul>", "</ul>"], '', $str) . "</ul>";
}
echo list_form($arr);
``` 

Как можно улучшить этот код для достижения поставленной задачи?
  • 26 ноября 2024 г. 13:13
Ответы на вопрос 2
Чтобы реализовать рекурсивную функцию, которая формирует список только с одним открывающим и одним закрывающим тегом `<ul>`, можно изменить вашу текущую функцию следующим образом.

Основная идея заключается в том, чтобы:
1. Добавить открывающий тег `<ul>` в самом начале функции.
2. Убедиться, что закрывающий тег `</ul>` добавляется только в конце, вне зависимости от количества вложенных массивов.

Вот пример реализации такой функции:

```php
function list_form(array $array) {
    $str = '<ul>'; // Начинаем с открывающего тега <ul>

    foreach ($array as $item) {
        if (is_array($item)) {
            $str .= '<li>' . htmlspecialchars($item['name']) . '</li>'; // Добавляем элемент списка
            
            // Если у элемента есть вложенные элементы
            if (!empty($item['items'])) {
                // Рекурсивно добавляем вложенные элементы
                $str .= list_form($item['items']);
            }
        } else {
            $str .= '<li>' . htmlspecialchars($item) . '</li>'; // Обрабатываем не массивы, если такие имеются
        }
    }

    $str .= '</ul>'; // Добавляем закрывающий тег </ul>
    return $str;
}

// Пример массива
$arr = [
    [
        'id' => 1,
        'name' => 'item1',
        'items' => [
            ['id' => 2, 'name' => 'item2', 'items' => []],
            ['id' => 3, 'name' => 'item3', 'items' => []],
            [
                'id' => 4,
                'name' => 'item4',
                'items' => [
                    ['id' => 5, 'name' => 'item5', 'items' => []],
                    ['id' => 6, 'name' => 'item6', 'items' => []]
                ]
            ]
        ]
    ]
];

echo list_form($arr);
```

### Что было изменено:
1. Убран цикл по ключам и значениям, вместо этого я стал обращаться непосредственно к элементам массива.
2. Начальный тег `<ul>` добавляется сразу в начале функции, а закрывающий тег `</ul>` добавляется в конце.
3. Используется `htmlspecialchars()` для предотвращения XSS при выводе данных.

Запустив приведенный код с вашим массивом, вы получите корректный вывод списка с учетом вложенности, формируя HTML-разметку в соответствии с вашими требованиями.
непонятно должна ли быть функция рекурсивная, сделал итеративную версию с состоянием: 
<?php
$arr = [ 'id' => 1, 'name' => 'item1', 'items' => [
	[ 'id' => 2, 'name' => 'item2', 'items' => [], ],
	[ 'id' => 3, 'name' => 'item3', 'items' => [], ],
	[ 'id' => 4, 'name' => 'item4', 'items' => [
		[ 'id' => 5, 'name' => 'item5', 'items' => [], ],
		[ 'id' => 6, 'name' => 'item6', 'items' => [], ],
	], ],
], ];

function list_form(array $input) {
	$result = [ '<ul>', ];
	$state = [ [ 'array' => $input, 'keys' => array_keys($input), 'idx' => 0, ], ];
	for (; count($state) > 1 || $state[0]['idx'] < count($state[0]['keys']); $state[0]['idx'] += 1) {
		if (count($state) > 1 && $state[0]['idx'] === count($state[0]['keys'])) {
			array_shift($state);
			continue;
		}
		$key = $state[0]['keys'][$state[0]['idx']];
		$value = $state[0]['array'][$key];
		if (!is_array($value)) {
			$result[] = "<li>$key => $value</li>";
		}
		else {
			$result[] = "<li>$key => []</li>";
			if (!count($value)) continue;
			array_unshift($state, [ 'array' => $value, 'keys' => array_keys($value), 'idx' => -1, ]);
		}
	}
	$result[] = '</ul>';
	return implode("\n", $result);
}

echo list_form($arr);


Похожие вопросы