Создание, продвижение сайтов
40-33-54

Создаем одномерный массив из многомерного

14 сентября 2019

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

Всё достаточно просто, если исходный массив у нас двумерный. Возьмём, для примера, такой:


 
  1. $arrIn = array(
  2.   array('a','b','c','d'),
  3.   array(1,2,3),
  4.   array('e',4,'f',5),
  5. );

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


 
  1. $arrOut = array();
  2. foreach($arrIn as $subArr){
  3.   $arrOut = array_merge($arrOut,$subArr);
  4. }

Можно конечно и поизвращаться, но больше для расширения кругозора, чем для реальной практики в данной задаче. Будем использовать две функций: array_map() - применяет callback-функцию к каждому элементу массива и функцию array_merge(), которую рассматривали выше.


 
  1. $arrOut = array();
  2. array_map(function($a) use(&$arrOut){
  3.   $arrOut = array_merge($arrOut,$a);
  4. }, $arrIn);

Немного расшифрую: в качестве callback-функции, мы используем анонимную функцию, которая получает поочерёдно каждый из вложенных массивов в переменную "$a" и сливает с массивом "$arrOut", который в итоге и будет содержать все значения исходного массива, но уже как одномерным. Так как внутри callback-функции массив "$arrOut" будет не виден (если только он не объявлен глобальным), мы используем ключевое слово use, которое позволяет использовать внешние переменные. Но передаём массив не как значение, а как "ссылку", поставив перед переменной символ амперсанда "&". Если бы мы этого не сделали, то каждый раз, мы получали бы пустой массив "$arrOut", который сливали с текущим "$a".
Для новичков всё это пока малопонятно и, пытаясь адаптировать код под свои нужды, могут наделать ошибок. Не пугайтесь - есть решение "в пару строк";) Используем функцию call_user_func_array(), которая, по сути, сделает всё то, что мы делали во втором примере, но, так сказать, одним махом.


 
  1. $arrOut = call_user_func_array('array_merge', $arrIn);

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


 
  1. Array
  2. (
  3.     [0] => a
  4.     [1] => b
  5.     [2] => c
  6.     [3] => d
  7.     [4] => 1
  8.     [5] => 2
  9.     [6] => 3
  10.     [7] => e
  11.     [8] => 4
  12.     [9] => f
  13.     [10] => 5
  14. )

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


 
  1. $arrOut = array();
  2. foreach($arrIn as $subArr){
  3.   $arrOut = array_merge($arrOut,array_values($subArr));
  4. }

Остаётся выяснить, какой из трёх вариантов работает быстрее. Я сгенерировал массив, состоящий из сотни вложенных массивов, в каждом из которых по десять элементов и вот средние результаты, которые говорят сами за себя:

Способ секунд
Цикл + array_merge 0.0109
array_map + array_merge 0.0170
call_user_func_array 0.0009

С двумерными массивами немного разобрались, теперь перейдём к более сложной задаче - трехмерные (и более) массивы или многоуровневые массивы с неизвестной вложенностью. И первое решение, которое напрашивается - это использование рекурсии.


 
  1. $arrIn = array( // исходный массив
  2.   'A' => array('first',2,3),
  3.   'B' => array(
  4.     'b1' => array(4,5,6,7),
  5.     'b2' => array('a','b','c')
  6.   ),
  7.   'C' => array(
  8.     0 => array(8,9),
  9.     1 => array(
  10.       'c01' => array(
  11.         array(10,11,12),
  12.         'last'
  13.       )
  14.     )
  15.   )
  16. );
  17. function makeSingleArray($arr){
  18.   if(!is_array($arr)) return false;
  19.   $tmp = array();
  20.   foreach($arr as $val){
  21.     if(is_array($val)){
  22.       $tmp = array_merge($tmp, makeSingleArray($val));
  23.     } else {
  24.       $tmp[] = $val;
  25.     }
  26.   }
  27.   return $tmp;
  28. }
  29. $arrOut = makeSingleArray($arrIn);

Вполне нормальный способ, который, на мой взгляд, не требует каких-то подробных разъяснений и хорошо справляется со своей задачей. Многие, даже не задумываясь, именно его бы и применили. Но я хочу показать, как это же можно сделать буквально парой строк кода, используя "итераторы" из стандартной библиотеки PHP (SPL):


 
  1. $iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($arrIn));
  2. $arrOut = iterator_to_array($iterator, false);

Вот и всё! И результат будет, как и в первом случае такой:


 
  1. Array
  2. (
  3.     [0] => first
  4.     [1] => 2
  5.     [2] => 3
  6.     [3] => 4
  7.     [4] => 5
  8.     [5] => 6
  9.     [6] => 7
  10.     [7] => a
  11.     [8] => b
  12.     [9] => c
  13.     [10] => 8
  14.     [11] => 9
  15.     [12] => 10
  16.     [13] => 11
  17.     [14] => 12
  18.     [15] => last
  19. )

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

Способ секунд
Рекурсия 0.2558
Классы итераторов 0.0346

Разница в скорости - более, чем в семь раз и кода меньше примерно во столько же! Дальнейшие комментарии излишни... ;)


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

  •  
  •  
  •  

Статьи

Сделайте заказ с сайта и получите 10% скидку на услугу

Заказ услуги с сайта raybin.ru

Нажимая на кнопку "Получить консультацию", я даю согласие на обработку персональных данных и соглашаюсь c условиями политики конфиденциальности
CAPTCHA
Этот вопрос задается для того, чтобы выяснить, являетесь ли Вы человеком или представляете из себя автоматическую спам-рассылку.
Заказать бесплатную SEO-консультацию

Запрос бесплатной SEO - консультации с сайта raybin.ru

Нажимая на кнопку "Получить консультацию", я даю согласие на обработку персональных данных и соглашаюсь c условиями политики конфиденциальности
CAPTCHA
Этот вопрос задается для того, чтобы выяснить, являетесь ли Вы человеком или представляете из себя автоматическую спам-рассылку.
Письмо в компанию Райбин

Письмо в компанию RAYBIN

Нажимая на кнопку "Получить консультацию", я даю согласие на обработку персональных данных и соглашаюсь c условиями политики конфиденциальности
CAPTCHA
Этот вопрос задается для того, чтобы выяснить, являетесь ли Вы человеком или представляете из себя автоматическую спам-рассылку.