Как "сгладить" многомерный массив в простой в PHP?

Вероятно, это вопрос новичка, но я уже давно изучаю документацию и не могу найти никакого решения. Я думал, что смогу использовать implode для каждого измерения, а затем соединить эти строки с str_split сделать новый простой массив. Тем не менее, я никогда не знаю, если шаблон соединения также не в значениях и так после выполнения str_split мои первоначальные ценности могут сломаться.

Есть ли что-то вроде combine($array1, $array2) для массивов внутри многомерного массива?

21 ответ

Решение

использованиеarray_walk_recursive

<?php

$aNonFlat = array(
    1,
    2,
    array(
        3,
        4,
        5,
        array(
            6,
            7
        ),
        8,
        9,
    ),
    10,
    11
);

$objTmp = (object) array('aFlat' => array());

array_walk_recursive($aNonFlat, create_function('&$v, $k, &$t', '$t->aFlat[] = $v;'), $objTmp);

var_dump($objTmp->aFlat);

/*
array(11) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
  [3]=>
  int(4)
  [4]=>
  int(5)
  [5]=>
  int(6)
  [6]=>
  int(7)
  [7]=>
  int(8)
  [8]=>
  int(9)
  [9]=>
  int(10)
  [10]=>
  int(11)
}
*/

?>

Протестировано с PHP 5.5.9-1ubuntu4.24 (cli) (сборка: 16 марта 2018 12:32:06)

$array  = your array

$result = call_user_func_array('array_merge', $array);

echo "<pre>";
print_r($result);

REF: http://php.net/manual/en/function.call-user-func-array.php

Вот еще одно решение (работает с многомерным массивом):

function array_flatten($array) {

   $return = array();
   foreach ($array as $key => $value) {
       if (is_array($value)){ $return = array_merge($return, array_flatten($value));}
       else {$return[$key] = $value;}
   }
   return $return;

}

$array  = Your array

$result = array_flatten($array);

echo "<pre>";
print_r($result);

Это одна строка, СУПЕР прост в использовании:

$result = array();
array_walk_recursive($original_array,function($v) use (&$result){ $result[] = $v; });

Это очень легко понять, внутри анонимной функции / замыкания. $v это ценность вашего $original_array,

Если у вас есть массив массивов, который не выходит за пределы одного уровня (пример использования, который я нахожу распространенным), вы можете обойтись без array_merge и оператор сплат.

<?php

$notFlat = [[1,2],[3,4]];
$flat = array_merge(...$notFlat);
var_dump($flat);

Выход:

array(4) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
  [3]=>
  int(4)
}

Оператор splat эффективно изменяет массив массивов на список массивов в качестве аргументов для array_merge,

// $array = your multidimensional array

$flat_array = array();

foreach(new RecursiveIteratorIterator(new RecursiveArrayIterator($array)) as $k=>$v){

$flat_array[$k] = $v;

}

Также задокументировано: http://www.phpro.org/examples/Flatten-Array.html

Извините за некробампинг, но ни один из предоставленных ответов не сделал того, что я интуитивно понял как "выравнивание многомерного массива". А именно этот случай:

[
  'a' => [
    'b' => 'value',
  ]
]

все предлагаемые решения сведут его к минимуму ['value'], но при этом теряется информация о тональности и глубине, плюс, если у вас есть еще одна клавиша "b" где-то еще, она перезапишет их.

Я хотел получить такой результат:

[
  'a_b' => 'value',
]

array_walk_recursive не передает информацию о рекурсивном ключе, поэтому я сделал это простой рекурсией:

function flatten($array, $prefix = '') {
    $return = [];
    foreach ($array as $key => $value) {
        if (is_array($value)) {
            $return = array_merge($return, flatten($value, $prefix . $key . '_'));
        } else {
            $return[$prefix . $key] = $value;
        }
    }
    return $return;
}

Измените префикс $ и разделитель '_' по своему вкусу.

Площадка здесь: https://3v4l.org/0B8hf

В PHP 7 вы можете использовать генераторы и делегирование генераторов (yield from) чтобы сгладить массив:

function array_flatten_iterator (array $array) {
    foreach ($array as $value) {
        if (is_array($value)) {
            yield from array_flatten_iterator($value);
        } else {
            yield $value;
        }
    }
}

function array_flatten (array $array) {
    return iterator_to_array(array_flatten_iterator($array), false);
}

Пример:

$array = [
    1,
    2,
    [
        3,
        4,
        5,
        [
            6,
            7
        ],
        8,
        9,
    ],
    10,
    11,
];    

var_dump(array_flatten($array));

http://3v4l.org/RU30W

Другой метод из комментариев пользователей PHP (упрощенно) и здесь:

function array_flatten_recursive($array) { 
   if (!$array) return false;
   $flat = array();
   $RII = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
   foreach ($RII as $value) $flat[] = $value;
   return $flat;
}

Большим преимуществом этого метода является то, что он отслеживает глубину рекурсии, если она вам понадобится при выравнивании.
Это выведет:

$array = array( 
    'A' => array('B' => array( 1, 2, 3)), 
    'C' => array(4, 5) 
); 
print_r(array_flatten_recursive($array)); 

#Returns: 
Array ( 
    [0] => 1 
    [1] => 2 
    [2] => 3 
    [3] => 4 
    [4] => 5 
)

Нерекурсивное решение (но разрушающее порядок):

function flatten($ar) {
    $toflat = array($ar);
    $res = array();

    while (($r = array_shift($toflat)) !== NULL) {
        foreach ($r as $v) {
            if (is_array($v)) {
                $toflat[] = $v;
            } else {
                $res[] = $v;
            }
        }
    }

    return $res;
}
function flatten_array($array, $preserve_keys = 0, &$out = array()) {
    # Flatten a multidimensional array to one dimension, optionally preserving keys.
    #
    # $array - the array to flatten
    # $preserve_keys - 0 (default) to not preserve keys, 1 to preserve string keys only, 2 to preserve all keys
    # $out - internal use argument for recursion
    foreach($array as $key => $child)
        if(is_array($child))
            $out = flatten_array($child, $preserve_keys, $out);
        elseif($preserve_keys + is_string($key) > 1)
            $out[$key] = $child;
        else
            $out[] = $child;
    return $out;
}

В PHP>=5.3 и на основании ответа Люка М (первого) вы можете использовать замыкания, подобные этому

array_walk_recursive($aNonFlat, function(&$v, $k, &$t){$t->aFlat[] = $v;}, $objTmp);

Мне это нравится, потому что мне не нужно заключать код функции в кавычки, как при использовании create_function()

Использование функций высшего порядка (примечание: я использую встроенные анонимные функции, которые появились в PHP 5.3):

function array_flatten($array) {
    return array_reduce(
        $array,
        function($prev, $element) {
            if (!is_array($element))
                $prev[] = $element;
            else
                $prev = array_merge($prev, array_flatten($element));
            return $prev;
        },
        array()
    );
}

Я нашел простой способ конвертировать многоуровневый массив в один. Я использую функцию "http_build_query", которая преобразует массив в строку URL. Затем разделите строку с помощью разнесения и декодируйте значение.

Вот образец.

$converted = http_build_query($data);
$rows = explode('&', $converted);
$output = array();
foreach($rows AS $k => $v){
   list($kk, $vv) = explode('=', $v);
   $output[ urldecode($kk) ] =  urldecode($vv);
}
return $output;
/*consider $mArray as multidimensional array and $sArray as single dimensional array
this code will ignore the parent array
*/

function flatten_array2($mArray) {
    $sArray = array();

    foreach ($mArray as $row) {
        if ( !(is_array($row)) ) {
            if($sArray[] = $row){
            }
        } else {
            $sArray = array_merge($sArray,flatten_array2($row));
        }
    }
    return $sArray;
}

Вы можете попробовать это:

function flat_an_array($a)
{
    foreach($a as $i)
    {
        if(is_array($i)) 
        {
            if($na) $na = array_merge($na,flat_an_array($i));
            else $na = flat_an_array($i);
        }
        else $na[] = $i;
    }
    return $na;
}

Просто простая функция для этого.

function deepFlatten($items)
{
    $result = [];
    foreach ($items as $item) {
        if (!is_array($item)) {
            $result[] = $item;
        } else {
            $result = array_merge($result, deepFlatten($item));
        }
    }

    return $result;
}

deepFlatten([1, [2], [[3], 4], 5]); // [1, 2, 3, 4, 5]

Если вы в порядке с потерей ключей массива, вы можете сгладить многомерный массив, используя рекурсивное замыкание в качестве обратного вызова, который использует array_values ​​(), убедившись, что этот обратный вызов является параметром для array_walk(), следующим образом.

<?php  

$array = [1,2,3,[5,6,7]];
$nu_array = null;
$callback = function ( $item ) use(&$callback, &$nu_array) {
    if (!is_array($item)) {
    $nu_array[] = $item;
    }
    else
    if ( is_array( $item ) ) {
     foreach( array_values($item) as $v) {
         if ( !(is_array($v))) {
             $nu_array[] = $v;
         }
         else
         { 
             $callback( $v );
         continue;
         }    
     }
    }
};

array_walk($array, $callback);
print_r($nu_array);

Единственным недостатком предыдущего примера является то, что он включает в себя написание гораздо большего количества кода, чем в следующем решении, которое использует array_walk_recursive() вместе с упрощенным обратным вызовом:

<?php  

$array = [1,2,3,[5,6,7]];

$nu_array = [];
array_walk_recursive($array, function ( $item ) use(&$nu_array )
                     {
                         $nu_array[] = $item;
                     }
);
print_r($nu_array);

Смотрите живой код

Этот пример кажется предпочтительнее предыдущего, скрывая подробности о том, как значения извлекаются из многомерного массива. Конечно, итерация происходит, но влечет ли она за собой рекурсию или управляющую структуру (структуры), вы узнаете только о прочтении array.c. Поскольку функциональное программирование фокусируется на вводе и выводе, а не на мелочах получения результата, несомненно, можно не беспокоиться о том, как происходит закулисная итерация, то есть до тех пор, пока перспективный работодатель не задаст такой вопрос.

Новый подход, основанный на функции предыдущего примера, представленной хаосом, которая исправляет ошибку перезаписи строковых ключей в мульти-массивах:

# Flatten a multidimensional array to one dimension, optionally preserving keys.
# $array - the array to flatten
# $preserve_keys - 0 (default) to not preserve keys, 1 to preserve string keys only, 2 to preserve all keys
# $out - internal use argument for recursion

function flatten_array($array, $preserve_keys = 2, &$out = array(), &$last_subarray_found) 
{
        foreach($array as $key => $child)
        {
            if(is_array($child))
            {
                $last_subarray_found = $key;
                $out = flatten_array($child, $preserve_keys, $out, $last_subarray_found);
            }
            elseif($preserve_keys + is_string($key) > 1)
            {
                if ($last_subarray_found)
                {
                    $sfinal_key_value = $last_subarray_found . "_" . $key;
                }
                else
                {
                    $sfinal_key_value = $key;
                }
                $out[$sfinal_key_value] = $child;
            }
            else
            {
                $out[] = $child;
            }
        }

        return $out;
}

Example:
$newarraytest = array();
$last_subarray_found = "";
$this->flatten_array($array, 2, $newarraytest, $last_subarray_found);

Вы можете использовать функцию flatten из нестандартной библиотеки PHP (NSPL). Работает с массивами и любыми повторяемыми структурами данных.

assert([1, 2, 3, 4, 5, 6, 7, 8, 9] === flatten([[1, [2, [3]]], [[[4, 5, 6]]], 7, 8, [9]]));

Простой подход.. Посмотрите это через рекурсию..

<?php

function flatten_array($simple){
static $outputs=array();
foreach ( $simple as $value)
{
if(is_array($value)){
    flatten_array($value);
}
else{
    $outputs[]=$value;
}

}
return $outputs;
}

$eg=['s'=>['p','n'=>['t']]];
$out=flatten_array($eg);
print_r($out);

?>

Кто-то может счесть это полезным, у меня возникла проблема со сглаживанием массива в каком-то измерении, я бы назвал его последним измерением, например, если у меня есть массив вроде:

array (
  'germany' => 
  array (
    'cars' => 
    array (
      'bmw' => 
      array (
        0 => 'm4',
        1 => 'x3',
        2 => 'x8',
      ),
    ),
  ),
  'france' => 
  array (
    'cars' => 
    array (
      'peugeot' => 
      array (
        0 => '206',
        1 => '3008',
        2 => '5008',
      ),
    ),
  ),
)

Или:

array (
  'earth' => 
  array (
    'germany' => 
    array (
      'cars' => 
      array (
        'bmw' => 
        array (
          0 => 'm4',
          1 => 'x3',
          2 => 'x8',
        ),
      ),
    ),
  ),
  'mars' => 
  array (
    'france' => 
    array (
      'cars' => 
      array (
        'peugeot' => 
        array (
          0 => '206',
          1 => '3008',
          2 => '5008',
        ),
      ),
    ),
  ),
)

Для обоих этих массивов, когда я вызываю метод ниже, я получаю результат:

array (
  0 => 
  array (
    0 => 'm4',
    1 => 'x3',
    2 => 'x8',
  ),
  1 => 
  array (
    0 => '206',
    1 => '3008',
    2 => '5008',
  ),
)

Итак, я сглаживаю последнее измерение массива, которое должно остаться прежним, метод ниже можно отредактировать, чтобы фактически остановиться на любом уровне:

function flattenAggregatedArray($aggregatedArray) {
    $final = $lvls = [];
    $counter = 1;
    $lvls[$counter] = $aggregatedArray;


    $elem = current($aggregatedArray);

    while ($elem){
        while(is_array($elem)){
            $counter++;
            $lvls[$counter] = $elem;
            $elem =  current($elem);
        }

        $final[] = $lvls[$counter];
        $elem = next($lvls[--$counter]);
        while ( $elem  == null){
            if (isset($lvls[$counter-1])){
                $elem = next($lvls[--$counter]);
            }
            else{
                return $final;
            }
        }
    }
}

Все это не сработало для меня... так что пришлось запустить его самому. работает просто отлично

function arrayFlat($arr){
$out = '';
    foreach($arr as $key => $value){

        if(!is_array($value)){
            $out .= $value.',';
        }else{
            $out .= $key.',';
            $out .= arrayFlat($value);
        }

    }
    return trim($out,',');
}


$result = explode(',',arrayFlat($yourArray));
echo '<pre>';
print_r($result);
echo '</pre>';

Заданный многомерный массив и преобразование его в одномерный, может быть сделано путем сброса всех значений, имеющих массивы, и сохранения их в первом измерении, например:

function _flatten_array($arr) {
  while ($arr) {
    list($key, $value) = each($arr); 
    is_array($value) ? $arr = $value : $out[$key] = $value;
    unset($arr[$key]);
  }
  return (array)$out;
}

Если вас интересуют только значения для одного конкретного ключа, этот подход может оказаться полезным:

function valuelist($array, $array_column) {
    $return = array();
    foreach($array AS $row){
        $return[]=$row[$array_column];
    };
    return $return;
};

Пример:

Учитывая $get_role_action=

array(3) {
  [0]=>
  array(2) {
    ["ACTION_CD"]=>
    string(12) "ADD_DOCUMENT"
    ["ACTION_REASON"]=>
    NULL
  }
  [1]=>
  array(2) {
    ["ACTION_CD"]=>
    string(13) "LINK_DOCUMENT"
    ["ACTION_REASON"]=>
    NULL
  }
  [2]=>
  array(2) {
    ["ACTION_CD"]=>
    string(15) "UNLINK_DOCUMENT"
    ["ACTION_REASON"]=>
    NULL
  }
}

чем $variables['role_action_list']=valuelist($get_role_action, 'ACTION_CD'); приведет к:

$variables["role_action_list"]=>
  array(3) {
    [0]=>
    string(12) "ADD_DOCUMENT"
    [1]=>
    string(13) "LINK_DOCUMENT"
    [2]=>
    string(15) "UNLINK_DOCUMENT"
  }

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

if( in_array('ADD_DOCUMENT', $variables['role_action_list']) ){
    //do something
};
Другие вопросы по тегам