Толик Панков
hex_laden
............ .................. ................
October 2025
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Сортировка многомерного массива в PHP.

Пояснение к задачке "Список стран".

Преамбула


Итак, имеется довольно простая учебная задача, отобразить на странице список стран с флагами, кодами ISO и наименованиями на английском и русском, необходимо обеспечить сортировку по столбцам базы данных (таблицы). В качестве хранилища данных выбираем текстовый файл формата CSV с разделителями "точка с запятой" (;). Готовый файл можно взять здесь или здесь. Для отображения флагов, понадобятся соответствующие картинки. Можно взять здесь или здесь
Форма ввода


Для выбора поля, по которому сортировать данные, а также, как их сортировать (по возрастанию или убыванию), необходима форма. Добавляем код, генерирующий форму в функцию print_sortform(), которая будет вызываться после функции print_top(), которая, в свою очередь, печатает заголовок HTML и заголовок таблицы.

function print_sortform()
{
	global $field;
	global $direction;
	
	$fields=array("ISOA2","ISOA3","ISON","EN","RU");
	$sortmess=array("Aplha-2","Alpha-3","Номер","Английский","Русский");
	
	echo "<tr><td colspan='12'><center><b>"."\n";
	echo "<form name='sort' method='get' action='iso.php'> Сортировать по:";
	$i=0;
	foreach ($fields as $f)
	{
		if ($field == $f)
		{
			echo "<nobr><input name='sort' type='radio' value='$f' checked> $sortmess[$i]</nobr> ";
		}
		else
		{
			echo "<nobr><input name='sort' type='radio' value='$f'> $sortmess[$i] </nobr>";
		}
		$i++;
	}
	echo "<br> Направление:";
	if ($direction == 0)
	{
		echo "<nobr><input name='direction' type='radio' value='0' checked> По возрастанию</nobr> ";
		echo "<nobr><input name='direction' type='radio' value='1'> По убыванию</nobr> ";
	}
	else
	{
		echo "<nobr><input name='direction' type='radio' value='0'> По возрастанию</nobr> ";
		echo "<nobr><input name='direction' type='radio' value='1' checked> По убыванию</nobr> ";
	}
	echo "<br> <input type='submit' value='Отправить'>"; 
	echo "</form></b></center></td></tr>";
}


Ранее в скрипте объявляем две глобальных переменных
$field = "ISOA2"; //поле, по которому будем сортировать
$direction = 0; //направление сортировки: 0 - по возрастанию, 
			//иначе - по убыванию


и два массива в самой функции - $fields и $sortmess, содержащие идентификаторы полей и информацию для отображения их в форме. Далее в цикле foreach формируем набор из переключателей (radiobutton) для выбора нужного поля.

Остальное в функции достаточно тривиально.

Загрузка данных


Данные будем хранить в многомерном массиве вот такой структуры:



Функция загрузки:

function load_data()
{	
	global $data;
	global $errmsg;
	
	$handle = fopen("isofull.csv","r");	
	if (!$handle)
	{
		$errmsg = "Can't open file isofull.csv";
		return false;
	}
	
	$databuf = array();	
	while (!feof($handle))
	{
		$readbuf = fgets($handle);		
		$databuf = explode(";", $readbuf);
		
		$data[] = array ("ISOA2" => $databuf[0],"ISOA3" => $databuf[1], "ISON" => $databuf[2],
				  "EN" => $databuf[3], "RU" => $databuf[4]);
	}	
	return true;
}


Заводим глобальную переменную $errmsg для того, чтоб туда писать сообщение об ошибке, и глобальный массив $data для нашей таблицы (БД), да, очевидный минус - я не стал париться с количеством элементов разбитой в массив строки. Но для PHP это не так, чтобы и критично. Если что просто вывалится с ошибкой. Некоторые вообще не заморачиваются обработкой ошибок в PHP, а отдают все на волю интерпретатора.

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

Передача параметров для сортировки массива


Передаем их в GET-параметрах. Значения параметров пишем в глобальные переменные.

if (isset($_GET['sort']))
{
	$field=$_GET['sort'];
}
if (isset($_GET['direction']))
{
	$direction=$_GET['direction'];
}


Проверять корректность переданных значений будем в функции сортировки.

Сортировка многомерного массива по указанному полю


В PHP имеется функция сортировки usort(); которая принимает массив данных, в качестве первого параметра, и функцию сортировки в качестве второго. Функцию сортировки описывает пользователь. В нее передается два элемента массива, а пользователь описывает алгоритм сравнения, таким образом, чтобы пользовательская функция выдавала 3 значения: -1: 1-й элемент < 2-го элемента, 0: 1-й элемент == 2-му элементу, 1: 1-й элемент > 2-го элемента. Пишем соответствующую пользовательскую функцию:

function compare ($a, $b)
{	
	global $field;
	global $direction;
	global $errmsg;
	
	if (!array_key_exists($field, $a))
	{
		$errmsg = "Field $field not found";
		return 0;
	}
	
	if ($direction == 0)
	{
		return strnatcmp($a[$field],$b[$field]);
	}
	else
	{
		return (strnatcmp($a[$field],$b[$field])*-1);
	}
}


1. В переменные $a и $b передаются два элемента массива, который надо отсортировать.

2. Далее, подключаемся к ранее заданным глобальным переменным:

$field
- поле таблицы (БД), по которому будем сортировать.
$direction - направление сортировки - 0 по возрастанию, другое значение - по убыванию.

3. Проверяем, есть ли соответствующий ключ в массиве, а это надо проверить, т.к. ключ передается в запросе, а в запросе может придти не то, что ожидает скрипт.

if (!array_key_exists($field, $a))
...


4. Проверяем, как сортировать и сортируем с помощью функции strnatcmp(). От стандартной strcmp() она отличается тем, что если ей попадается набор чисел в виде строк, то она их будет сортировать в формате, обычном для человека:

strnatcmp():

4
8
10
12
16
20
24
28


strcmp():

10
100
104
108
112
116
12
120
124


strnatcpm()strcpm()), как раз возвразащают 0, если аргументы равны, -1 если 1-й > 2-го, и 1, если 2-й > 1-го.

Чтобы поменять порядок сортировки на обратный, достаточно поменять результат работы функции на обратный, что можно сделать, умножив результат функции strnatcmp()/strcmp() на -1:

(strnatcmp($a[$field],$b[$field])*-1)

Результат


Скриншот:



Исходник
Посмотреть, как работает

Известные баги


- Нет флагов некоторых редких стран (может быть потом сам нарисую).
- Почему-то страны на русскую букву Р криво сортируются, если сортировать по русским наименованиям стран.

Источники


usort()

Это репост с сайта http://tolik-punkoff.com
Оригинал: http://tolik-punkoff.com/2020/09/15/sortirovka-mnogomernogo-massiva-v-php/

From:
(will be screened)
Identity URL: 
имя пользователя:    
Вы должны предварительно войти в LiveJournal.com
 
E-mail для ответов: 
Вы сможете оставлять комментарии, даже если не введете e-mail.
Но вы не сможете получать уведомления об ответах на ваши комментарии!
Внимание: на указанный адрес будет выслано подтверждение.
Username:
Password:
Subject:
No HTML allowed in subject
Message:



Notice! This user has turned on the option that logs IP addresses of anonymous posters.