Разминка для мозга. Как в PHP создать многомерный массив из строки по разделителю?

Silentland

Новичок
Массив A имеет вид:
PHP:
  array("aa_bb_cc" => 123, "dd_ee" => 456, ...)
Нужно превратить его в массив B вида:
PHP:
array("aa" => array("bb" => array("cc" => 123))), "dd"=>array("ee" => 456), ...)
Уровней вложенности может быть сколько угодно, разделитель только "_"

Можно определить функцию и создать рекурсию на 30 строчек... Но как-то громоздко это.

Мое решение такое:
PHP:
$A = array("aa_bb_cc" => 123, "aa_ee" => null);
    
foreach ($A as $field => $value) {
   $arr = explode("_", $field);
   $str = '$B';
   for ($i = 0; $i < count($arr); $i++) {
       $str .= '[$arr[' . $i . ']]';
   }
   $str = '$alias=&' . $str . ';';
   eval($str);
   $alias = $value;
}
echo print_r ($B, true);
У кого-нибудь есть мысли как написать изящнее?
 

AmdY

Пью пиво
Команда форума
PHP:
function kissSetArrayData(&$data, $key, $value){    
 $key = (array)$key;    
  $needData = & $data;
  for ($i = 0; $i < count($key) - 1; $i++) {        
    $needData = & $needData[$key[$i]];    
  }    
  $needData[$key[$i]] = $value;
}
$data = [];
kissSetArrayData($data, ['aa', 'bb', 'cc'], 123);
kissSetArrayData($data, ['dd', 'ee'], null);
var_dump($data);
 

Silentland

Новичок
Предложили упрощение моего способа:
PHP:
$str = '$B=array();';    
foreach ($A as $field => $value) {
    $str .= '$B["' . str_replace('_','"]["', $field).'"]=' . var_export($value, true) . ';';
}
eval($str);
---
Пытаюсь догнать, что означает kissSetArrayData($data, ['aa', 'bb', 'cc'], 123); :) loading...
 

fixxxer

К.О.
Партнер клуба
с евалом - жесть.

У AmdY показан единственно правильный подход к решению такого рода задач
 

Silentland

Новичок
Блин, не догоняю зачем целовать данные. Это не для средних умов) Куда совать массив A, где получать массив B?
 
  • Like
Реакции: craz

Silentland

Новичок
с евалом - жесть.
У AmdY показан единственно правильный подход к решению такого рода задач
Почему жесть?

В чем смысл подхода? Где там разбор по разделителю? Как понимаю, AmdY отрицает сериализацию/десериализацию как таковую?
 

fixxxer

К.О.
Партнер клуба
разбор по разделителю там предполагается что ты в состоянии сделать сам

вот так наверное будет понятнее
PHP:
function arraySep2dim(array $in, $separator = '_') {
    $result = array();
    foreach ($in as $key => $value) {
        $ptr = &$result;
        foreach (explode($separator, $key) as $token) {
            $ptr = &$ptr[$token];
        }
        $ptr += is_array($value) ? arraySep2dim($value, $separator) : $value;
    }
    return $result;
}

$a = array( "aa_bb_cc" => 123, "dd_ee" => 456, "dd" => array("xx_yy" => 999) );
var_dump( arraySep2dim($a) );
евал - это ЖЕСТЬ. его нельзя использовать никогда.
 

AmdY

Пью пиво
Команда форума
Silentland
kiss это префикс, так как писалось до неймспейсов. обозначает то же что в моей подписи, а не поцелуи.
я отрицаю eval как вещь небезопасную и плохо отлаживаемую.
про серриализацию не понял.
 

Silentland

Новичок
вот так наверное будет понятнее
Ага. Ну и AmdY разбил в уме массив на элементы, а их по разделителю. Теперь догнал.
евал - это ЖЕСТЬ. его нельзя использовать никогда. + я отрицаю eval как вещь небезопасную и плохо отлаживаемую.
Ну фиг знает, я же не из космоса данные беру. Ну все говорят жесть, пусть будет жесть. Потом догоню почему так.
про серриализацию не понял.
Это я не увидел синтаксического разбора и подумал, что вы отрицаете такой вид задач как класс.
 

fixxxer

К.О.
Партнер клуба
Ну все говорят жесть, пусть будет жесть. Потом догоню почему так.
Помимо уже перечисленных проблем с безопасностью и отладкой, подумай, какой объем работы выполняет php, делая eval.
 

Silentland

Новичок
Кстати, функция не работает как надо.
PHP:
var_dump(arraySep2dim(array( 'A' => "", 'bb_B1' => "", 'bb_B2' => "абв", 'cc_C1' => null)));
выдает все значения int(0)
 

Silentland

Новичок
ага += надо делать только если value массив
Да, просто с = все работает. Подробно ковыряю примеры. Начал с AmdY
PHP:
$A = array( 'A' => "", 'bb_B1' => "", 'bb_B2' => "абв", 'cc_C1' => null);
$result = array();
foreach ($A as $key => $value) {        
    $key = explode('_', $key);  
    $needData = & $result;
    for ($i = 0; $i < count($key) - 1; $i++) {        
        $needData = & $needData[$key[$i]];    
    }    
    $needData[$key[$i]] = $value;
}
var_dump($result);
Выдает array(3) { ["A"]=> string(0) "" ["bb"]=> array(2) { ["B1"]=> string(0) "" ["B2"]=> string(6) "абв" } ["cc"]=> &array(1) { ["C1"]=> NULL } }
Никак не могу сообразить откуда берется указатель в последнем элементе &array(1) { ["C1"]=> NULL }

Так же не ясно почему это работает
function kissSetArrayData(&$data, $key, $value)
у меня на PHP 5.4? Там же должна быть запрещена передача по ссылке?
 

fixxxer

К.О.
Партнер клуба
нет, функция может объявлять, что принимает аргумент по ссылке

запрещена передача по ссылке при вызове:

т.е.
function f($v) { .. }
f(&$v) - так нельзя.

function f(&$v) { .. }
f($v) - ок
 

uid

Новичок
Вот кусочек кода из моего велосипеда:
PHP:
	public static function setItem(array $arr, $path, $value, $delim = '.') {
		$path = explode($delim, $path);
		$current =& $arr;
		foreach ($path as $item) {
			if (!isset($current[$item]) || !is_array($current[$item])) {
				$current[$item] = array();
			}
			$current = & $current[$item];
		}
		$current = $value;
		return $arr;
	}
Использование:
PHP:
$data = Arr::setItem($data, 'item.subitem.test.key', 'value');
Если во входном массиве были какие-то элементы - они сохранятся, функция их не меняет.
 

uid

Новичок
Можно было бы и по ссылке передавать, но в этом случае читабельность и логичность кода ухудшаются.
 
Сверху