Работа с массивами в PHP: правильный путь
Russian (Pусский) translation by Stanislav Protasevich (you can also view the original English article)
В этом уроке мы рассмотрим практические примеры работы с рядом PHP-шных функций для работы с массивами. Этот материал пригодится каждому PHP разработчику: позволит писать компактный и читабельный код.
Все примеры можно будет скачать, кликнув на соответствующие ссылки и поделиться с коллегами.
Основы
Давайте начнём с простой функции, которая оперирует ключами и значениями элементов массивов. Одной из таких функций является array_combine(), которая создаёт новый массив из двух существующих: первый использует для создания ключей, второй в качестве значений:
1 |
$keys = ['sky', 'grass', 'orange']; |
2 |
$values = ['blue', 'green', 'orange']; |
3 |
|
4 |
$array = array_combine($keys, $values); |
5 |
print_r($array); |
6 |
|
7 |
// Array
|
8 |
// (
|
9 |
// [sky] => blue
|
10 |
// [grass] => green
|
11 |
// [orange] => orange
|
12 |
// )
|
В этом же разрезе вам могут пригодиться функции array_values(). Она извлекает из ассоциативного массива значения; array_keys() возвращает только ключи заданного массива; array_flip() меняет местами ключи и значения:
1 |
print_r(array_keys($array)); // ['sky', 'grass', 'orange'] |
2 |
print_r(array_values($array)); // ['blue', 'green', 'orange'] |
3 |
print_r(array_flip($array)); |
4 |
|
5 |
// Array
|
6 |
// (
|
7 |
// [blue] => sky
|
8 |
// [green] => grass
|
9 |
// [orange] => orange
|
10 |
// )
|
Сокращение кода
Функция list(), которая по сути является конструкцией языка, позволяет быстро присвоить значения массива ряду переменных. Простой пример работы с функцией list():
1 |
// define array
|
2 |
$array = ['a', 'b', 'c']; |
3 |
|
4 |
// without list()
|
5 |
$a = $array[0]; |
6 |
$b = $array[1]; |
7 |
$c = $array[2]; |
8 |
|
9 |
// with list()
|
10 |
list($a, $b, $c) = $array; |
Данный приём блестяще вписывается в совместную работу с функциями preg_slit() или explode() . Извлекать можно только те значения, которые действительно нужны:
1 |
$string = 'hello|wild|world'; |
2 |
list($hello, , $world) = explode('|', $string); |
3 |
echo("$hello, $world"); // hello, world |
Также функцию list() можно поместить в foreach:
1 |
$arrays = [[1, 2], [3, 4], [5, 6]]; |
2 |
|
3 |
foreach ($arrays as list($a, $b)) { |
4 |
$c = $a + $b; |
5 |
echo($c . ', '); // 3, 7, 11, |
6 |
}
|
Чтобы извлечь значения из ассоциативного массива можно воспользоваться функцией extract(). В результате вы получите целый ряд переменных (имена которых совпадают с ключами массива):
1 |
$array = [ |
2 |
'clothes' => 't-shirt', |
3 |
'size' => 'medium', |
4 |
'color' => 'blue', |
5 |
];
|
6 |
|
7 |
extract($array); |
8 |
|
9 |
echo("$clothes $size $color"); // t-shirt medium blue |
При работе с функцией extract() следует быть осторожным, особенно во время взаимодействия с пользовательскими данными (результатами запросов), поэтому рекомендуется использовать флаги EXTR_IF_EXISTS и EXTR_PREFIX_ALL.
Чтобы сделать противоположное действие можно воспользоваться функцией compact(), которая сформирует массив из ряда переменных:
1 |
$clothes = 't-shirt'; |
2 |
$size = 'medium'; |
3 |
$color = 'blue'; |
4 |
|
5 |
$array = compact('clothes', 'size', 'color'); |
6 |
print_r($array); |
7 |
|
8 |
// Array
|
9 |
// (
|
10 |
// [clothes] => t-shirt
|
11 |
// [size] => medium
|
12 |
// [color] => blue
|
13 |
// )
|
Функции фильтрации
Для фильтрации данных лучшим образом подойдёт функция array_filter(). В качестве параметров необходимо передать массив и анонимную функцию-обработчик. Для того чтобы оставить элемент в массиве следует вернуть true, в обратном случае false:
1 |
$numbers = [20, -3, 50, -99, 55]; |
2 |
|
3 |
$positive = array_filter($numbers, function($number) { |
4 |
return $number > 0; |
5 |
});
|
6 |
|
7 |
print_r($positive); // [0 => 20, 2 => 50, 4 => 55] |
Фильтрацию можно произвести и по ключам. Для этого следует воспользоваться третьим параметром, флагом ARRAY_FILTER_USE_KEY или ARRAY_FILTER_USE_BOTH.
Вдобавок к этому, можете вызвать array_filter() без аргументов. В результате из массива будут удалены все пустые значения:
1 |
$numbers = [-1, 0, 1]; |
2 |
|
3 |
$not_empty = array_filter($numbers); |
4 |
|
5 |
print_r($not_empty); // [0 => -1, 2 => 1] |
Для того чтобы получить из массива только уникальные значения следует вызвать функцию array_unique(). Стоит отметить, что в результирующий массив войдут только первые найденные элементы:
1 |
$array = [1, 1, 1, 1, 2, 2, 2, 3, 4, 5, 5]; |
2 |
$uniques = array_unique($array); |
3 |
|
4 |
print_r($uniques); |
5 |
|
6 |
// Array
|
7 |
// (
|
8 |
// [0] => 1
|
9 |
// [4] => 2
|
10 |
// [7] => 3
|
11 |
// [8] => 4
|
12 |
// [9] => 5
|
13 |
// )
|
Функция array_column() будет полезна если вам необходимо извлечь определённый столбец многомерного массива. Это может быть результат SQL запроса или данные из файла CSV. Вам следует указать массив и название колонки:
1 |
$array = [ |
2 |
['id' => 1, 'title' => 'tree'], |
3 |
['id' => 2, 'title' => 'sun'], |
4 |
['id' => 3, 'title' => 'cloud'], |
5 |
];
|
6 |
|
7 |
$ids = array_column($array, 'id'); |
8 |
|
9 |
print_r($ids); // [1, 2, 3] |
В PHP 7 функция array_column() получила небольшой апгрэйд: возможность взаимодействия с полями объекта. Это в значительной мере упрощает работу с массивами моделей:
1 |
$cinemas = Cinema::find()->all(); |
2 |
$cinema_ids = array_column($cinemas, 'id'); // php7 forever! |
Обход массивов
array_map() позволяет обойти все элементы массива и указать функцию обратного вызова. Вы можете передать как анонимку, так и название существующей функции. В результате получите массив с преобразованными значениями:
1 |
$cities = ['Berlin', 'KYIV', 'Amsterdam', 'Riga']; |
2 |
$aliases = array_map('strtolower', $cities); |
3 |
|
4 |
print_r($aliases); // ['berlin', 'kyiv, 'warsaw', 'riga'] |
5 |
|
6 |
$numbers = [1, -2, 3, -4, 5]; |
7 |
$squares = array_map(function($number) { |
8 |
return $number ** 2; |
9 |
}, $numbers); |
10 |
|
11 |
print_r($squares); // [1, 4, 9, 16, 25] |
Существует устойчивое заблуждение, что в функцию обратного вызова нельзя одновременно передавать и ключи, и значения. Это не так:
1 |
$model = ['id' => 7, 'name'=>'James']; |
2 |
|
3 |
$callback = function($key, $value) { |
4 |
return "$key is $value"; |
5 |
};
|
6 |
|
7 |
$res = array_map($callback, array_keys($model), $model); |
8 |
print_r($res); |
9 |
|
10 |
// Array
|
11 |
// (
|
12 |
// [0] => id is 7
|
13 |
// [1] => name is James
|
14 |
// )
|
Это не самая лучшая реализация. Давайте воспользуемся функцией array_walk(). Она похожа на array_map(), но работает чуть иначе. Во-первых, исходные данные передаются по ссылке, в результате чего array_walk() обработает переданный массив, а не создаст новый. Таким образом мы сможем передать значения и ключи исходного массива по ссылке:
1 |
$fruits = [ |
2 |
'banana' => 'yellow', |
3 |
'apple' => 'green', |
4 |
'orange' => 'orange', |
5 |
];
|
6 |
|
7 |
array_walk($fruits, function(&$value, $key) { |
8 |
$value = "$key is $value"; |
9 |
});
|
10 |
|
11 |
print_r($fruits); |
12 |
|
13 |
// Array
|
14 |
// (
|
15 |
// [banana] => banana is yellow
|
16 |
// [apple] => apple is green
|
17 |
// [orange] => orange is orange
|
18 |
// )
|
Объединение массивов
Вызов функции array_merge() — самый просто способ объединить несколько массивов. Все данные будут слиты воедино, повторяющиеся данные перезапишутся:
1 |
$array1 = ['a' => 'a', 'b' => 'b', 'c' => 'c']; |
2 |
$array2 = ['a' => 'A', 'b' => 'B', 'D' => 'D']; |
3 |
|
4 |
$merge = array_merge($array1, $array2); |
5 |
print_r($merge); |
6 |
// Array
|
7 |
// (
|
8 |
// [a] => A
|
9 |
// [b] => B
|
10 |
// [c] => c
|
11 |
// [D] => D
|
12 |
// )
|
Для того чтобы удалить значения одного массива из другого массива воспользуйтесь функцией array_diff(). Обратная операция реализуется при помощи вызова array_intersect(). Примеры:
1 |
$array1 = [1, 2, 3, 4]; |
2 |
$array2 = [3, 4, 5, 6]; |
3 |
|
4 |
$diff = array_diff($array1, $array2); |
5 |
print_r($diff); // [0 => 1, 1 => 2] |
6 |
|
7 |
$intersect = array_intersect($array1, $array2); |
8 |
print_r($intersect); // [2 => 3, 3 => 4] |
Математические операции
С помощью функции array_sum() можно посчитать сумму элементов массива; array_product() перемножит все значения; array_reduce() позволит применить свою собственную формулу:
1 |
$numbers = [1, 2, 3, 4, 5]; |
2 |
|
3 |
echo(array_sum($numbers)); // 15 |
4 |
echo(array_product($numbers)); // 120 |
5 |
|
6 |
echo(array_reduce($numbers, function($carry, $item) { |
7 |
return $carry ? $carry / $item : 1; |
8 |
})); // 0.0083 = 1/2/3/4/5 |
Функция array_count_values() поможет посчитать количество всех уникальных значений массива:
1 |
$things = ['apple', 'apple', 'banana', 'tree', 'tree', 'tree']; |
2 |
$values = array_count_values($things); |
3 |
|
4 |
print_r($values); |
5 |
|
6 |
// Array
|
7 |
// (
|
8 |
// [apple] => 2
|
9 |
// [banana] => 1
|
10 |
// [tree] => 3
|
11 |
// )
|
Генерирование массивов
Для того чтобы сформировать массив из заданного количества элементов, и задать им единое значение следует воспользоваться функцией array_fill()
1 |
$bind = array_fill(0, 5, '?'); |
2 |
print_r($bind); // ['?', '?', '?', '?', '?'] |
Чтобы получить массив, элементы которого должно варьировать в диапазоне часов или букв следует вызывать функцию range():
1 |
$letters = range('a', 'z'); |
2 |
print_r($letters); // ['a', 'b', ..., 'z'] |
3 |
|
4 |
$hours = range(0, 23); |
5 |
print_r($hours); // [0, 1, 2, ..., 23] |
Чтобы обрезать массив — получить первые три элемента — достаточно вызвать функцию array_slice():
1 |
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; |
2 |
$top = array_slice($numbers, 0, 3); |
3 |
print_r($top); // [1, 2, 3] |
Сортировка массивов
Следует знать, что все PHP-шные функции сортировки работают с исходными массивами по ссылке и возвращают true в случае успеха и false при неудаче. Функция sort() сортирует элементы массива в возрастающем порядке и присваивает новые ключи элементам массива. Разновидности сортировки в зависимости от префиксной буквы:
- a, сортировка, сохраняющая отношения между ключами и значениями
- k, сортировка по ключам
- r, сортировка в прямом/обратном порядке
- u, сортировка при помощи пользовательской функции
Доступны комбинации видов сортировки:
| a | k | r | u | |
| a | asort | arsort | uasort | |
| k | ksort | krsort | ||
| r | arsort | krsort | rsort | |
| u | uasort | usort |
Работаем с массивами как профи
Поистине крутые вещи начинают происходить, когда мы комбинируем несколько вышеупомянутых функций. К примеру мы можем убрать из массива все пустые значения, вызвав array_filter() и array_map():
1 |
$values = ['say ', ' bye', ' ', ' to', ' spaces ', ' ']; |
2 |
|
3 |
$words = array_filter(array_map('trim', $values)); |
4 |
print_r($words); // ['say', 'bye', 'to', 'spaces'] |
Чтобы получить идентификаторы и названия объектов моделей достаточно вызывать array_combine() и array_column():
1 |
$models = [$model1, $model2, $model3]; |
2 |
|
3 |
$id_to_title = array_combine( |
4 |
array_column($models, 'id'), |
5 |
array_column($models, 'title') |
6 |
);
|
Подсчёт трёх самых часто используемых элемента массива можно осуществить вызовом array_count_values(), arsort() и array_slice():
1 |
$letters = ['a', 'a', 'a', 'a', 'b', 'b', 'c', 'd', 'd', 'd', 'd', 'd']; |
2 |
|
3 |
$values = array_count_values($letters); // get key to count array |
4 |
arsort($values); // sort descending preserving key |
5 |
$top = array_slice($values, 0, 3); // get top 3 |
6 |
|
7 |
print_r($top); |
8 |
// Array
|
9 |
// (
|
10 |
// [d] => 5
|
11 |
// [a] => 4
|
12 |
// [b] => 2
|
13 |
// )
|
Комбинация функций array_sum() и array_map() позволит с лёгкостью подсчитать сумму товаров в корзине:
1 |
$order = [ |
2 |
['product_id' => 1, 'price' => 99, 'count' => 1], |
3 |
['product_id' => 2, 'price' => 50, 'count' => 2], |
4 |
['product_id' => 2, 'price' => 17, 'count' => 3], |
5 |
];
|
6 |
|
7 |
$sum = array_sum(array_map(function($product_row) { |
8 |
return $product_row['price'] * $product_row['count']; |
9 |
}, $order)); |
10 |
|
11 |
print_r($sum); // 250 |
Заключение
Данный урок — лишний повод убедиться в том, что знание элементарных функций поможет сделать ваш код более ёмким и читабельным. Конечно же это далеко не весь список функций для работы с массивами, которые есть в PHP. Ещё больше возможностей открываются в случае использования специальных параметров и флагов. В этом уроке мы рассмотрели основы основ, о которых должен знать каждый PHP разработчик.
Все примеры, приведённые в этом уроке доступны в виде презентации. Ссылка в дополнительных материалах.
Не стесняйтесь задавать вопросы в разделе комментариев.



