Как поменять местами элементы массива

Код меняет местами первые два элемента массива $city (Москва и Питер):

$city = array(0 => 'Москва', 1 => 'Питер', 2 => 'Вологда');
list($city[0], $city[1]) = array($city[1], $city[0]);
print_r($city);

Результат:

Array
(
    [0] => Питер
    [1] => Москва
    [2] => Вологда
)

Добавлено: 26 Июля 2018 21:54:36 Добавил: Андрей Ковальчук

Программа: Печать массива в виде HTML-таблицы

Преобразование массива в таблицу с горизонтально расположенными столбцами располагает фиксированное количество элементов в строке.

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

Функция pc_grid_horizontal(), показанная в примере 4.8, позволяет указать массив и число столбцов. Она предполагает ширину таблицы, равную 100%, но ее можно изменить с помощью переменной $table_width.

Пример 4.8. pc_grid_horizontal()

function pc_grid_horizontal($array, $size) {

// вычисляем ширину элемента <td> в процентах
$table_width = 100;
$width = intval($table_width / $size);

// определяем вид тегов <tr> и <td>
// функция sprintf() требует использования %% для получения символа %
$tr = '<tr align="center">';
$td = "<td width=\"$width%%\">%s</td>";

// открываем таблицу
$grid = "<table width=\"$table_width%\">$tr";

// выполняем цикл по элементам и отображаем в строке длиной $sized
// $i отслеживает, когда нужно начинать новую строку таблицы
$i = 0;
foreach ($array as $e) {
$grid .= sprintf($td, $e);
$i++;

// конец строки
// закрываем ее и начинаем новую
if (!($i % $size)) {
$grid .= "</tr>$tr";
}
}

// заполняем остальные ячейки пробелами
while ($i % $size) {
$grid .= sprintf($td, ' ');
$i++;
}

// добавляем </tr> при необходимости
$end_tr_len = strlen($tr) * -1;
if (substr($grid, $end_tr_len) != $tr) {
$grid .= '</tr>';
} else {
$grid = substr($grid, 0, $end_tr_len);
}// закрываем таблицу
$grid .= '</table>';
return $grid;
}


Функция начинается с вычисления ширины каждого элемента <td> в процентах к общей ширине таблицы. В зависимости от количества столбцов и общего размера, сумма ширины элементов <td> может не совпадать с шириной элемента <table>, но это не должно заметно влиять на отображение HTML. Затем определяются теги <td> и <tr>, при этом используется нотация форматирования в стиле функции printf. Для получения символа %, необходимого для выражения в процентах ширины элемента <td>, используйте сдвоенный символ %%.

Ядро функции – это цикл foreach по элементам массива, в котором каждый тег td> добавляется к переменной $grid. При достижении конца строки, что происходит, когда общее число обработанных элементов становится кратным количеству элементов в строке, элемент <tr> закрывается и открывается снова.

После того как добавлены все элементы, необходимо заполнить последнюю строку пробелами или пустыми элементами <td>. Для корректной передачи таблицы в броузер поместите непрерывную пробельную строку в ячейку данных, вместо того чтобы оставлять ее пустой. Теперь убедитесь в отсутствии лишних элементов <tr> в конце сетки,
что может произойти, когда количество элементов становится кратным ширине (другими словами, если не нужно добавлять заполняющие ячейки). Наконец, можно закрыть таблицу.

Например, напечатаем названия 50 штатов США в таблице из шести столбцов:

// устанавливаем соединение с базой данных
$dsn = 'mysql://user:password@localhost/table';
$dbh = DB::connect($dsn);
if (DB::isError($dbh)) { die ($dbh->getMessage()); }
// запрашиваем в базе данных информацию о 50-ти штатах
$sql = "SELECT state FROM states";
$sth = $dbh->query($sql);
// загружаем данные из базы данных в массив
while ($row = $sth->fetchRow(DB_FETCHMODE_ASSOC)) {
$states[] = $row['state'];
}
// генерируем HTML-таблицу
$grid = pc_grid_horizontal($states, 6);
// и печатаем ее
print $grid;

Добавлено: 21 Июля 2018 11:08:45 Добавил: Андрей Ковальчук

Нахождение всех перестановок массива

Задача
Есть массив элементов, и необходимо вычислить все возможные варианты упорядочения массива.

Решение
Используйте один из двух алгоритмов перестановки, обсуждаемых далее.

Обсуждение
Функция pc_permute(), приведенная в примере 4.6, – это PHP-модификация базовой рекурсивной функции.

Пример 4.6. pc_permute()

function pc_permute($items, $perms = array()) {
if (empty($items)) {
print join(' ', $perms) . "\n";
} else {
for ($i = count($items) - 1; $i >= 0; --$i) {
$newitems = $items;
$newperms = $perms;
list($foo) = array_splice($newitems, $i, 1);
array_unshift($newperms, $foo);
pc_permute($newitems, $newperms);
}
}
}


Например:

pc_permute(split(' ', 'she sells seashells'));
she sells seashells
she seashells sells
sells she seashells
sells seashells she
seashells she sells
seashells sells she

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

Впрочем, функция pc_next_permutation(), показанная в примере 4.7, немного приятнее. Она объединяет идею Марка-Джейсона Доминуса (Mark-Jason Dominus) из «Perl Cookbook» Тома Кристиансена (Tom Christianson) и Натана Торкингтона (Nathan Torkington) (издательство O’Reilly) с алгоритмом из классического труда Эдсгера Дейкстры(Edsger Dijkstra) «A Discipline of Programming» (издательство PrenticeHall).

Пример 4.7. pc_next_permutation()

function pc_next_permutation($p, $size) {
// проходим массив сверху вниз в поисках числа, которое меньше следующего
for ($i = $size - 1; $p[$i] >= $p[$i+1]; --$i) { }

// если такого нет, прекращаем перестановки
// массив перевернут: (1, 2, 3, 4) => (4, 3, 2, 1)
if ($i == -1) { return false; }

// проходим массив сверху вниз в поисках числа,
// превосходящего найденное ранее
for ($j = $size; $p[$j] <= $p[$i]; --$j) { }

// переставляем их
$tmp = $p[$i]; $p[$i] = $p[$j]; $p[$j] = $tmp;

// теперь переворачиваем массив путем перестановки элементов,
// начиная с конца
for (++$i, $j = $size; $i < $j; ++$i, --$j) {
$tmp = $p[$i]; $p[$i] = $p[$j]; $p[$j] = $tmp;
}
return $p;
}
$set = split(' ', 'she sells seashells'); // подобно массиву ('she',
'sells', 'seashells')
$size = count($set) - 1;
$perm = range(0, $size);
$j = 0;
do {
foreach ($perm as $i) { $perms[$j][] = $set[$i]; }
} while ($perm = pc_next_permutation($perm, $size) and ++$j);
foreach ($perms as $p) {
print join(' ', $p) . "\n";
}


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

Однако этот прием имеет некоторые недостатки. Для нас, программистов на PHP, более важными являются частые извлечения, вставки и
объединения массивов, т. е. то, что для Perl является центральным.

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

Алгоритм Дейкстры решает это, принимая перестановку ряда целых чисел и возвращая следующую наибольшую перестановку. Код оптимизируется на основе этого предположения. Начиная с наименьшего шаблона (который представлен просто целыми числами, расположенными по возрастанию) и продолжая выполнение снизу вверх, можно прокрутить все перестановки за один раз, передавая предыдущую перестановку обратно в функцию для получения следующей. Едва ли там имеют место хоть какие-то перестановки, даже в последнем цикле перестановки, где переставляется конец.

Метод предоставляет дополнительные преимущества. Рецепт Доминуса требует общего количества перестановок для данного шаблона. Так как оно равно факториалу количества элементов в множестве, то требует довольно больших вычислительных затрат, даже с промежуточным хранением результата. Вместо определения этого числа быстрее вернуть значение false из функции pc_next_permutation(), если окажется, что $i == -1. Когда это происходит, вы вынуждены покинуть массив, исчерпав перестановки данной фразы.

Два последних замечания по реализации. Поскольку размер множества – это константа, то он определяется однажды с помощью функции count() и передается в функцию pc_next_permutation(); это быстрее, чем повторно вызывать функцию count() внутри функции. Кроме того, так как уникальность элементов множества гарантируется самой его
структурой, т. е. в нем одно и только одно вхождение каждого целого числа, то нет необходимости проводить проверку на равенство внутри первых двух циклов for. Однако это нужно делать при использовании этого рецепта для других числовых множеств, где могут встречаться дубликаты.

Добавлено: 21 Июля 2018 11:07:26 Добавил: Андрей Ковальчук

Определение всех комбинаций элементов массива

Задача
Необходимо определить совокупность всех комбинаций множеств, содержащих все или некоторые элементы массива, известную также как показательное множество.

Решение
Используйте функцию pc_array_power_set()

Пример 4.5. pc_array_power_set()

function pc_array_power_set($array) {
// инициализируем пустым множеством
$results = array(array());
foreach ($array as $element)
foreach ($results as $combination)
array_push($results, array_merge(array($element), $combination));
return $results;
}


Она возвращает массив массивов, содержащий все комбинации элементов, включая пустое множество. Например:

$set = array('A', 'B', 'C');
$power_set = pc_array_power_set($set);
Массив $power_set состоит из восьми массивов:
array();
array('A');
array('B');
array('C');
array('A', 'B');
array('A', 'C');
array('B', 'C');
array('A', 'B', 'C');


Обсуждение
Сначала включаем в результат пустое множество {}. Все-таки должна быть одна комбинация множеств, которая не содержит ни одного элемента из них.

Остальная часть этой функции основана на природе комбинаций и реализации оператора foreach в PHP. Каждый новый элемент, добавленный в массив, увеличивает число комбинаций. Новые комбинации представляют собой старые комбинации с новым элементом; двухэлементный массив, состоящий из A и B, генерирует четыре возможных
комбинации: {}, {A}, {B} и {A, B}. Добавление C к этому множеству сохраняет четыре предыдущих комбинации, а также добавляет четыре новых комбинации: {C}, {A, C}, {B, C} и {A, B, C}.

Таким образом, внешний цикл foreach проходит по всем элементам списка; внутренний цикл foreach проходит по всем предыдущим комбинациям, созданным более ранними элементами. Это немного сложно, но необходимо точно знать, как ведет себя PHP в процессе выпол-
нения цикла foreach.

Функция array_merge() объединяет элемент с предыдущими комбинациями. Заметим, однако, что массив $results, добавленный к новому массиву функцией array_push(), – это тот самый массив, который обра-батывался в цикле foreach. Обычно добавление элемента к массиву $results приводит к бесконечному циклу, но не в PHP, поскольку PHP работает с копией исходного списка. И когда вы возвращаетесь на уровень внешнего цикла и снова выполняете цикл foreach со следующим элементом, эта копия сбрасывается. Поэтому можно работать непосредственно с массивом $results и использовать его в качестве стека для хранения комбинаций. Хранение всей информации в массиве даст большую гибкость, когда в дальнейшем придется выводить на печать и дополнительно подразделять на комбинации.

Чтобы исключить пустое множество, замените начальную строку:

// инициализируем, добавляя пустое множество
$results = array(array());

на:
// инициализируем, добавляя первый элемент
$results = array(array(array_pop($array)));


Поскольку одноэлементный массив имеет только одну комбинацию – самого себя, то удаление элемента равносильно выполнению первого прохода цикла. Двойные циклы foreach не знают, что в действительности они начинают свою работу со второго элемента массива.

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

$array = array('Adam', 'Bret', 'Ceff', 'Dave');
foreach (pc_array_power_set($array) as $combination) {
print join("\t", $combination) . "\n";
}


Ниже показано, как напечатать только трехэлементные комбинации:
foreach (pc_array_power_set($set) as $combination) {
if (3 == count($combination)) {
print join("\t", $combination) . "\n";
}
}


Итерация с большими множествами занимает значительное время. Множество из n элементов приводит к образованию 2n+1 множеств. Другими словами, увеличение n на 1 вызывает удвоение количества элементов.

Добавлено: 21 Июля 2018 11:06:31 Добавил: Андрей Ковальчук

Определение объединения, пересечения или разности двух массивов

Задача
Есть два массива, и требуется найти их объединение (все элементы, но если элемент входит в оба массива, он учитывается один раз), пересечение (элементы, входящие в оба массива) или разность (элементы одного массива, не присутствующие в другом).

Решение
Для определения объединения:

$union = array_unique(array_merge($a, $b));


Для вычисления пересечения:
$intersection = array_intersection($a, $b);


Для нахождения простой разности:
$difference = array_diff($a, $b);


И для получения симметрической разности (исключающее ИЛИ):
$difference = array_merge(array_diff($a, $b), array_diff($b, $a));


Обсуждение
Многие из необходимых для таких вычислений компонентов встроены в PHP, нужно только объединить их в соответствующей последовательности.

При получении объединения из двух массивов создается один гигантский массив со всеми значениями исходных массивов. Но функция array_merge() разрешает дубликаты значений при объединении двух числовых массивов, поэтому нужно вызвать функцию array_unique(),
чтобы отфильтровать такие элементы. Но при этом могут образоваться пропуски, поскольку функция array_unique() не уплотняет массив. Однако это не представляет затруднения, поскольку и оператор foreach, и функция each() без помех обрабатывают редко заполненные массивы.

Функция для вычисления пересечения имеет простое имя array_intersection() и не требует дополнительных усилий.

Функция array_diff() возвращает массив, содержащий все уникальные элементы массива $old, которые не входят в массив $new. Это называется простой разностью:
$old = array('To', 'be', 'or', 'not', 'to', 'be');
$new = array('To', 'be', 'or', 'whatever');
$difference = array_diff($old, $new);
Array
(
=> not
=> to
)


Результирующий массив $difference содержит 'not' и 'to', так как функция array_diff() чувствительна к регистру. В него не входит элемент 'whatever', поскольку его нет в массиве $old.

Чтобы получить обратную разность, или, другими словами, найти уникальные элементы массива $new, отсутствующие в массиве $old, нужно поменять местами аргументы:
$old = array('To', 'be', 'or', 'not', 'to', 'be');
$new = array('To', 'be', 'or', 'whatever');
$reverse_diff = array_diff($new, $old);
Array
(
=> whatever
)


Массив $reverse_diff содержит только элемент 'whatever'.

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

// применим нечувствительный к регистру алгоритм вычитания; разность -i
$seen = array();
foreach ($new as $n) {
$seen[strtolower($n)]++;
}
foreach ($old as $o) {
$o = strtolower($o);
if (!$seen[$o]) { $diff[$o] = $o; }
}


Первый оператор foreach создает ассоциативный массив для дальнейшего поиска. Затем выполняется цикл по массиву $old и, если в процессе поиска элемент не найден, то он добавляется в массив $diff.

Этот процесс можно ускорить, объединив функции array_diff() и array_map():

$diff = array_diff(array_map('strtolower', $old),
array_map('strtolower', $new));


Симметрическая разность – это то, что принадлежит $a, но не принадлежит $b, плюс то, что есть в $b, но нет в $a:

$difference = array_merge(array_diff($a, $b), array_diff($b, $a));


Однажды установленный, алгоритм движется вперед. Функция array_diff() вызывается дважды и определяет две разности. Затем они объединяются в один массив. Нет необходимости вызывать функцию array_unique(), так как эти массивы были специально сконструированы как не имеющие общих элементов.

Добавлено: 21 Июля 2018 11:05:28 Добавил: Андрей Ковальчук

Удаление двойных элементов из массива

Задача
Необходимо удалить дубликаты из массива.

Решение
Если массив уже заполнен, используйте функцию array_unique(), которая возвращает новый массив, не содержащий двойных значений:

$unique = array_unique($array);


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

foreach ($_REQUEST['fruits'] as $fruit) {
if (!in_array($array, $fruit)) { $array[] = $fruit; }
}


Тот же метод для ассоциативных массивов:
foreach ($_REQUEST['fruits'] as $fruit) {
$array[$fruit] = $fruit;
}


Обсуждение
Если процесс завершен, то применение функции array_unique() является лучшим способом удаления дубликатов. Но если вы внутри цикла, то предупредить появление двойных элементов можно с помощью проверки, не находятся ли уже эти элементы в массиве.

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

В действительности, быстрее использовать метод ассоциативного массива, а затем применить функцию array_values() к результату (или, коли на то пошло, array_keys(), но array_values() немного быстрее), чем непосредственно создавать числовой массив через голову функции in_array().

Добавлено: 21 Июля 2018 11:04:07 Добавил: Андрей Ковальчук

Тасование колоды карт

Задача
Необходимо перетасовать колоду карт и раздать их.

Решение
Создайте массив из 52 целых чисел, перемешайте его и свяжите с картами:

$suits = array('Clubs', 'Diamonds', 'Hearts', 'Spades');
$cards = array('Ace', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'Jack', 'Queen', 'King');
$deck = pc_array_shuffle(range(0, 51));
(($draw = array_pop($deck)) != NULL) {
print $cards[$draw / 4] . ' of ' . $suits[$draw % 4] . "\n";
}


Этот код использует функцию pc_array_shuffle() из рецепта 4.20.

Обсуждение
Для английского представления карт создается пара массивов, $suits и $cards. Числа от 0 до 51 случайным образом расставляются и назначаются массиву $deck. Чтобы сдать карту, достаточно извлечь число из начала массива, рассматривая этот массив как буквальную колоду карт.

Необходимо добавить проверку на значение NULL внутри оператора while, иначе цикл прервется, когда вы вытащите нулевую карту. Если изменить колоду так, чтобы она содержала числа от 1 до 52, то, с математической точки зрения, сопоставление чисел и карт становится более сложным.

Чтобы сдать несколько карт сразу, вызовите функцию array_slice():

array_slice($deck, $cards * -1);

Добавлено: 21 Июля 2018 11:03:19 Добавил: Андрей Ковальчук

Рандомизация массива

Задача
Необходимо перетасовать элементы массива в случайном порядке.

Решение
Если у вас запущена версия PHP 4.3 или выше, то используйте функцию shuffle():

shuffle($array);


С более ранними версиями используйте функцию pc_array_shuffle(),
показанную в примере 4.4.

Пример 4.4. pc_array_shuffle()

function pc_array_shuffle($array) {
$i = count($array);
while(--$i) {
$j = mt_rand(0, $i);
if ($i != $j) {
// перестановка элементов
$tmp = $array[$j];
$array[$j] = $array[$i];
$array[$i] = $tmp;
}
}
return $array;
}


Ниже приведены примеры:

$cards = range(1,52); // deal out 52 "cards"
$cards = pc_array_shuffle($cards);


Обсуждение
В PHP уже существует функция shuffle() для перемешивания массивов, однако в PHP 4.2.2 она работает некорректно. Встроенный алгоритм перемешивания имеет тенденцию давать предпочтение одним определенным перестановкам перед другими. Последние перестановки выглядят случайными, но так как элементы в каждой конкретной позиции имеют разные шансы оказаться в конце процесса, то такая перетасовка является недостоверной. Это исправлено в PHP 4.3.

Функция pc_array_shuffle(), известная как перестановка Фишера-Йетса, одинаково распределяет элементы вдоль массива. Используйте ее с версиями PHP более ранними, чем 4.3. В отличие от shuffle( ), эта функция возвращает перемешанный массив, а не изменяет его по месту. Она также требует плотно упакованного массива с целочисленными ключами.

Добавлено: 21 Июля 2018 07:27:33 Добавил: Андрей Ковальчук

Сортировка массива с использованием метода вместо функции

Задача
Необходимо определить пользовательскую процедуру сортировки массива. Однако вместо функции нужно применить метод объекта.

Решение
Передайте массив, содержащий имя класса и метода вместо имени функции:

usort($access_times, array('dates', 'compare'));


Обсуждение
Как и в случае с пользовательской функцией, метод объекта должен принять два входных аргумента, а возвратить значение 1, 0 или –1 – в зависимости от того, больше ли первый аргумент второго, равен ли ему или меньше:

class pc_sort {
// обратный порядок сравнения строки
function strrcmp($a, $b) {
return strcmp($b, $a);
}
}
usort($words, array('pc_sort', 'strrcmp'));

Добавлено: 21 Июля 2018 07:26:47 Добавил: Андрей Ковальчук

Сортировка множества массивов

Задача
Необходимо отсортировать несколько массивов или многомерный массив.

Решение
Используйте функцию array_multisort():

Чтобы одновременно отсортировать несколько массивов, передайте это множество массивов функции array_multisort():

$colors = array('Red', 'White', 'Blue');
$cities = array('Boston', 'New York', 'Chicago');
array_multisort($colors, $cities);
print_r($colors);
print_r($cities);
Array
(
[0] => Blue
=> Red
=> White
)
Array
(
[0] => Chicago
=> Boston
=> New York
)


Чтобы отсортировать несколько измерений внутри одного массива, передайте определенные элементы массива:

$stuff = array('colors' => array('Red', 'White', 'Blue'),
'cities' => array('Boston', 'New York', 'Chicago'));
array_multisort($stuff['colors'], $stuff['cities']);
print_r($stuff);
Array
(
[colors] => Array
(
[0] => Blue
=> Red
=> White
)
[cities] => Array
(
[0] => Chicago
=> Boston
=> New York
)
)


Чтобы изменить тип сортировки, как в функции sort(), передайте SORT_REGULAR, SORT_NUMERIC или SORT_STRING после имени массива. Для изменения порядка сортировки, в отличие от функции sort(), передайте SORT_ASC или SORT_DESC после имени массива. Можно передать как тип, так и порядок сортировки.

Обсуждение
Функция array_multisort() может сортировать несколько массивов одновременно или многомерный массив по одному или более направлений. Массивы трактуются как колонки таблицы, которая должна быть отсортирована по строкам. Первый массив является главным массивом для сортировки; порядок всех элементов других массивов устанавливается на основе порядка сортировки первого массива. Если элементы первого
массива равны, то их порядок определяется вторым массивом, и т. д.

По умолчанию устанавливаются значения сортировки SORT_REGULAR и SORT_ASC, и они переустанавливаются после каждого массива, поэтому нет необходимости передавать какое-либо из этих значений, разве что для ясности.

$numbers = array(0, 1, 2, 3);
$letters = array('a', 'b', 'c', 'd');
array_multisort($numbers, SORT_NUMERIC, SORT_DESC,
$letters, SORT_STRING , SORT_DESC);


Код этого примера обращает массивы.

Добавлено: 21 Июля 2018 07:26:05 Добавил: Андрей Ковальчук

Сортировка массива по вычисляемому полю

Задача
Необходимо задать собственную процедуру сортировки.

Решение
Используйте функцию usort() в комбинации с пользовательской
функцией:

// сортируем в порядке, обратном естественному
function natrsort($a, $b) {
return strnatcmp($b, $a);
}
$tests = array('test1.php', 'test10.php', 'test11.php', 'test2.php');
usort($tests, 'natrsort');


Обсуждение
Функция сравнения должна возвращать значение больше 0, если $a > $b, равное 0, если $a == $b и значение меньше 0, если $a < $b. Для сортиров ки в обратном порядке делайте наоборот. Функция strnatcmp(), приведенная в разделе «Решение», подчиняется этим правилам.

Чтобы поменять направление сортировки на обратное, вместо умножения возвращаемого значения strnatcmp($a, $b) на -1 поменяйте местами аргументы в функции strnatcmp($b, $a).
Функции сортировки не обязательно должны быть оболочками существующей сортировки. Например, функция pc_date_sort(), приведенная в примере 4.2, показывает, как сортировать даты.

Пример 4.2. pc_date_sort()
// ожидаем дату в формате "MM/DD/YYYY"
function pc_date_sort($a, $b) {
list($a_month, $a_day, $a_year) = explode('/', $a);
list($b_month, $b_day, $b_year) = explode('/', $b);
if ($a_year > $b_year ) return 1;
if ($a_year < $b_year ) return -1;
if ($a_month > $b_month) return 1;
if ($a_month < $b_month) return -1;
if ($a_day > $b_day ) return 1;
if ($a_day < $b_day ) return -1;
return 0;
}
$dates = array('12/14/2000', '08/10/2001', '08/07/1999');
usort($dates, 'pc_date_sort');


Во время сортировки функция usort() часто – каждый раз, когда ей надо сравнить два элемента – выполняет пересчет значений, возвращаемых функцией сортировки, что замедляет процесс. Для того чтобы избежать ненужной работы, можно кэшировать сравниваемые значения, как показано в примере 4.3.

Пример 4.3. pc_array_sort()
function pc_array_sort($array, $map_func, $sort_func = '') {
$mapped = array_map($map_func, $array); // cache $map_func() values
if ('' == $sort_func) {
asort($mapped); // функция asort() быстрее функции usort()
} else {
uasort($mapped, $sort_func); // необходимо сохранить ключи
}
while (list($key) = each($mapped)) {
$sorted[] = $array[$key]; // используем отсортированные ключи
}
return $sorted;
}


Чтобы избежать ненужной работы, функция pc_array_sort() использует временный массив $mapped для кэширования возвращаемых значений. Затем она сортирует массив $mapped, используя или порядок сортировки по умолчанию, или определенную пользователем процедуру сортировки. Важно, что она использует сортировку, сохраняющую связи ключ/значение. По умолчанию она использует функцию asort(), потому что она быстрее, чем функция uasort(). (Медленность функции uasort() это все-таки значительный довод в пользу функции pc_array_sort().) Наконец, она создает отсортированный массив $sorted,
при этом отсортированные ключи в массиве $mapped выступают в ка-
честве индексов значений исходного массива.

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

function pc_u_length($a, $b) {
$a = strlen($a);
$b = strlen($b);
if ($a == $b) return 0;
if ($a > $b) return 1;
return -1;
}
function pc_map_length($a) {
return strlen($a);
}
$tests = array('one', 'two', 'three', 'four', 'five',
'six', 'seven', 'eight', 'nine', 'ten');
// для количества элементов < 5 функция pc_u_length() быстрее
usort($tests, 'pc_u_length');
// для количества элементов >= 5 функция pc_map_length() быстрее
$tests = pc_array_sort($tests, 'pc_map_length');


Здесь функция pc_array_sort() быстрее функции usort(), поскольку
массив достигает длины в пять элементов.

Добавлено: 21 Июля 2018 07:25:11 Добавил: Андрей Ковальчук

Сортировка массива

Задача
Необходимо отсортировать массив определенным образом.

Решение
Для сортировки массива в общепринятом смысле этого слова используйте функцию sort():

$states = array('Delaware', 'Pennsylvania', 'New Jersey');
sort($states);


Для сортировки в числовом порядке передайте SORT_NUMERIC в качестве второго аргумента функции sort().

$scores = array(1, 10, 2, 20);
sort($scores, SORT_NUMERIC);


Числа будут отсортированы в порядке возрастания (1, 2, 10, 20) вместо лексикографического порядка (1, 10, 2, 20).

Обсуждение
Функция sort() не сохраняет связи ключ/значение между элементами; вместо этого она реиндексирует элементы, начиная с 0 по возрастанию. (Единственным исключением является одноэлементный массив; индекс его единственного элемента не сбрасывается в 0. Это исправлено, начиная с версии PHP 4.2.3.)

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

$states = array(1 => 'Delaware', 'Pennsylvania', 'New Jersey');
asort($states);
while (list($rank, $state) = each($states)) {
print "$state was the #$rank state to join the United States\n";
}


Используйте функцию natsort() для упорядочения массива по естественному алгоритму сортировки. При естественной сортировке можно смешивать строки и числа внутри элементов и получать при этом правильный результат.

$tests = array('test1.php', 'test10.php', 'test11.php', 'test2.php');
natsort($tests);


Теперь элементы расположены по порядку: 'test1.php', 'test2.php', 'test10.php' и 'test11.php'. При естественной сортировке число 10 располагается после числа 2; обычная сортировка приведет к противоположному результату. Для выполнения нечувствительной к регистру
естественной сортировки используйте функцию natcasesort().

Чтобы отсортировать массив в обратном порядке, используйте функцию rsort() или функцию arsort(), которая действует подобно функции rsort(), но к тому же сохраняет ключи. Не существует функции natrsort() или natcasersort(). В эти функции можно также передать в качестве аргумента выражение SORT_NUMERIC.

Добавлено: 21 Июля 2018 07:23:46 Добавил: Андрей Ковальчук

Обращение массива

Задача
Необходимо изменить порядок расположения элементов массива на
обратный.

Решение
Используйте функцию array_reverse():

$array = array('Zero', 'One', 'Two');
$reversed = array_reverse($array);


Обсуждение
Функция array_reverse() изменяет порядок следования элементов массива на обратный.

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

for ($i = 0, $size = count($array); $i < $size; $i++) {
...
}


делайте так:

for ($i = count($array) - 1; $i >=0 ; $i--) {
...
}


Однако, как всегда, применяйте цикл for только для плотно упакованных массивов.

Другой альтернативой изменения порядка элементов может быть размещение их в массиве. Например, при заполнении массива рядом строк из базы данных можно модифицировать запрос с помощью выражения ORDER DESC. Уточнить синтаксис можно в руководстве по базе данных.

Добавлено: 21 Июля 2018 07:22:48 Добавил: Андрей Ковальчук

Нахождение элементов, удовлетворяющих определенному критерию

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

Решение
Используйте цикл foreach:

$movies = array(...);
foreach ($movies as $movie) {
if ($movie['box_office_gross'] < 5000000) { $flops[] = $movie; }
}


Или функцию array_filter():
$movies = array(...);
function flops($movie) {
return ($movie['box_office_gross'] < 5000000) ? 1 : 0;
}
$flops = array_filter($movies, 'flops');


Обсуждение
Цикл foreach прост – вы прокручиваете данные и добавляете в возвращаемый массив элементы, которые удовлетворяют вашему критерию.Если нужен только первый такой элемент, то используйте break для выхода из цикла:

foreach ($movies as $movie) {
if ($movie['box_office_gross'] > 200000000) { $blockbuster = $movie;
break; }
}


Можно также выйти прямо из функции:

function blockbuster($movies) {
foreach ($movies as $movie) {
if ($movie['box_office_gross'] > 200000000) { return $movie; }
}
}


Однако при использовании функции array_filter() сначала нужно создать функцию обратного вызова, которая возвращает true для значений, которые нужно сохранить, и false в противном случае. После вызова функции array_filter() нужно, чтобы PHP обработал массив таким же образом, как он обрабатывался в операторе цикла foreach.

Из функции array_filter() невозможно выйти раньше времени, поэтому оператор foreach предоставляет большую гибкость и его проще понять. К тому же это один из немногих случаев, когда встроенная функция PHP не имеет явного превосходства над кодом пользовательского уровня.

Добавлено: 21 Июля 2018 07:22:02 Добавил: Андрей Ковальчук

Определение позиции элемента в массиве

Задача
Необходимо узнать, присутствует ли элемент в массиве, и если да, то в какой позиции он находится.

Решение
Используйте функцию array_search(). Она возвращает ключ обнаруженного элемента или значение false:

$position = array_search($array, $value);
if ($position !== false) {
//элемент массива $array в позиции $position имеет значение $value
}


Обсуждение
Используйте функцию in_array() для установления наличия в массиве определенного значения; используйте функцию array_search() для определения местоположения этого значения. Однако поскольку функция array_search() довольно изящно обрабатывает ситуации, когда значение не найдено, то вместо функции in_array() лучше использовать функцию array_search(). Разница в скорости работы незначительная, а дополнительная информация может оказаться полезной:

$favorite_foods = array(1 => 'artichokes', 'bread', 'cauliflower',
'deviled eggs');
$food = 'cauliflower';
$position = array_search($food, $favorite_foods);
if ($position !== false) {
echo "My #$position favorite food is $food";
} else {echo "Blech! I hate $food!";
}


Используйте оператор !== для сравнения со значением false, поскольку если обнаруженная строка находится на нулевой позиции, то оператор if преобразует его в логическое значение false, что явно не то, что ожидалось.

Если значение встречается в массиве несколько раз, то единственное, что гарантирует функция array_search(), – это возвращение одного значения, но не обязательно первого по порядку.

Добавлено: 21 Июля 2018 07:21:01 Добавил: Андрей Ковальчук