У меня есть данные CSV, загруженные в многомерный массив. Таким образом, каждая «строка» представляет собой запись, а каждый «столбец» содержит данные одного и того же типа. Я использую функцию ниже, чтобы загрузить свой файл CSV.
function f_parse_csv($file, $longest, $delimiter)
{
$mdarray = array();
$file = fopen($file, "r");
while ($line = fgetcsv($file, $longest, $delimiter))
{
array_push($mdarray, $line);
}
fclose($file);
return $mdarray;
}
Мне нужно указать столбец для сортировки, чтобы он переставлял строки. Один из столбцов содержит информацию о дате в формате Y-m-d H:i:s, и я хотел бы иметь возможность отсортировать, чтобы самая последняя дата была первой строкой.
@deceze, stackoverflow.com/q/1597736/1709587 мне кажется лучшей мишенью для обмана. Это более точный дубликат, и ответы там, следовательно, переходят к сути быстрее, чем ваш на stackoverflow.com/a/17364128/1709587, при этом в совокупности имеют тот же уровень детализации. Что вы скажете о смене цели? (Раскрытие информации: возможно, я предвзято являюсь автором одного из ответов на предлагаемую мной цель обмана.)






Функция "Usort" - ваш ответ. http://php.net/usort
Я проголосовал против вас из-за того, что вы не смогли предоставить решение с объяснением и примерами исходного вопроса. Обновите свой ответ, и я отменю свой голос.
С усорт. Вот общее решение, которое можно использовать для разных столбцов:
class TableSorter {
protected $column;
function __construct($column) {
$this->column = $column;
}
function sort($table) {
usort($table, array($this, 'compare'));
return $table;
}
function compare($a, $b) {
if ($a[$this->column] == $b[$this->column]) {
return 0;
}
return ($a[$this->column] < $b[$this->column]) ? -1 : 1;
}
}
Чтобы отсортировать по первому столбцу:
$sorter = new TableSorter(0); // sort by first column
$mdarray = $sorter->sort($mdarray);
Я получаю ошибку синтаксического анализа: ошибка синтаксического анализа, неожиданный T_STRING, ожидание T_OLD_FUNCTION или T_FUNCTION или T_VAR или '}' во второй строке этого класса.
Замените «protrected» на «var» и «__construct» на «TableSorter», и он будет работать в PHP4. Обратите внимание, однако, что PHP4 больше не поддерживается.
Я установил PHP на v5, не знал, что по умолчанию работает v4. Посмотрев на это некоторое время, я думаю, что понимаю, как изменить его для разных типов.
Я предпочитаю использовать array_multisort. См. Документацию здесь.
Вы можете использовать array_multisort ()
Попробуйте что-то вроде этого:
foreach ($mdarray as $key => $row) {
// replace 0 with the field's index/key
$dates[$key] = $row[0];
}
array_multisort($dates, SORT_DESC, $mdarray);
Для PHP> = 5.5.0 просто извлеките столбец для сортировки. Нет необходимости в петле:
array_multisort(array_column($mdarray, 0), SORT_DESC, $mdarray);
Итак, в этом примере $ mdarray может быть двумерным массивом, например массивом записей базы данных. В этом примере 0 - это индекс столбца «дата» в каждой записи (или строке). Итак, вы создаете массив $ date (в основном тот же массив, но только с этим столбцом) и указываете функции array_multisort сортировать $ mdarray на основе значений этого конкретного столбца.
Для ясности вы можете добавить в начало этого примера $dates = array();
Должен ли array_multisort работать с ассоциативными массивами (изменение $row[0] на $row['whatever']? Не сюда. После того, как я изменил свой массив на числовой, функция работала, как ожидалось.
Разве включение $key не является ненужным при использовании array_multisort ()? Кажется, проще и умышленнее написать foreach ($mdarray as $row) { $sortByDate[] = $row['date']; }, чем array_multisort( $sortByDate, SORT_DESC, $mdarray ); (ваш семантический пробег может быть разным).
Если ответ array_multi_sort(), вопрос не был понят. Хотя технически это будет работать, обычно есть лучшее решение с функцией сравнения, созданной пользователем, и использованием функции usort(). Легче в обслуживании. С помощью мультисортировки вы обычно создаете код, который подготавливает данные для сортировки. Если структура данных изменится, этот код может быть выброшен. С usort () вы изменяете функцию сравнения точно так же, как вы меняли структуру данных.
На странице PHP array_multisort есть более обширный пример этого ответа: php.net/manual/en/function.array-multisort.php#example-4928.
Предупреждение: array_multisort (): размеры массивов несовместимы
Вот класс php4 / php5, который сортирует одно или несколько полей:
// a sorter class
// php4 and php5 compatible
class Sorter {
var $sort_fields;
var $backwards = false;
var $numeric = false;
function sort() {
$args = func_get_args();
$array = $args[0];
if (!$array) return array();
$this->sort_fields = array_slice($args, 1);
if (!$this->sort_fields) return $array();
if ($this->numeric) {
usort($array, array($this, 'numericCompare'));
} else {
usort($array, array($this, 'stringCompare'));
}
return $array;
}
function numericCompare($a, $b) {
foreach($this->sort_fields as $sort_field) {
if ($a[$sort_field] == $b[$sort_field]) {
continue;
}
return ($a[$sort_field] < $b[$sort_field]) ? ($this->backwards ? 1 : -1) : ($this->backwards ? -1 : 1);
}
return 0;
}
function stringCompare($a, $b) {
foreach($this->sort_fields as $sort_field) {
$cmp_result = strcasecmp($a[$sort_field], $b[$sort_field]);
if ($cmp_result == 0) continue;
return ($this->backwards ? -$cmp_result : $cmp_result);
}
return 0;
}
}
/////////////////////
// usage examples
// some starting data
$start_data = array(
array('first_name' => 'John', 'last_name' => 'Smith', 'age' => 10),
array('first_name' => 'Joe', 'last_name' => 'Smith', 'age' => 11),
array('first_name' => 'Jake', 'last_name' => 'Xample', 'age' => 9),
);
// sort by last_name, then first_name
$sorter = new Sorter();
print_r($sorter->sort($start_data, 'last_name', 'first_name'));
// sort by first_name, then last_name
$sorter = new Sorter();
print_r($sorter->sort($start_data, 'first_name', 'last_name'));
// sort by last_name, then first_name (backwards)
$sorter = new Sorter();
$sorter->backwards = true;
print_r($sorter->sort($start_data, 'last_name', 'first_name'));
// sort numerically by age
$sorter = new Sorter();
$sorter->numeric = true;
print_r($sorter->sort($start_data, 'age'));
Это работает только с ассоциативными массивами?
да - только ассоциативные массивы. Теперь, когда я смотрю на это, это неправильное решение этой проблемы.
Прежде чем я смог запустить класс TableSorter, я придумал функцию, основанную на том, что предоставил Шинхан.
function sort2d_bycolumn($array, $column, $method, $has_header)
{
if ($has_header) $header = array_shift($array);
foreach ($array as $key => $row) {
$narray[$key] = $row[$column];
}
array_multisort($narray, $method, $array);
if ($has_header) array_unshift($array, $header);
return $array;
}Я знаю, что прошло 2 года с тех пор, как этот вопрос был задан и дан ответ, но вот еще одна функция, которая сортирует двумерный массив. Он принимает переменное количество аргументов, что позволяет вам передавать более одного ключа (т. Е. Имени столбца) для сортировки. Требуется PHP 5.3.
function sort_multi_array ($array, $key)
{
$keys = array();
for ($i=1;$i<func_num_args();$i++) {
$keys[$i-1] = func_get_arg($i);
}
// create a custom search function to pass to usort
$func = function ($a, $b) use ($keys) {
for ($i=0;$i<count($keys);$i++) {
if ($a[$keys[$i]] != $b[$keys[$i]]) {
return ($a[$keys[$i]] < $b[$keys[$i]]) ? -1 : 1;
}
}
return 0;
};
usort($array, $func);
return $array;
}
Попробуйте здесь: http://www.exorithm.com/algorithm/view/sort_multi_array
Можно ли заменить первые 3 строки функции на $keys = func_get_args(); array_unshift($keys);?
Вот еще один подход, использующий uasort () и анонимную функцию обратного вызова (закрытие). Я использовал эту функцию регулярно. Требуется PHP 5.3 - больше никаких зависимостей!
/**
* Sorting array of associative arrays - multiple row sorting using a closure.
* See also: http://the-art-of-web.com/php/sortarray/
*
* @param array $data input-array
* @param string|array $fields array-keys
* @license Public Domain
* @return array
*/
function sortArray( $data, $field ) {
$field = (array) $field;
uasort( $data, function($a, $b) use($field) {
$retval = 0;
foreach( $field as $fieldname ) {
if ( $retval == 0 ) $retval = strnatcmp( $a[$fieldname], $b[$fieldname] );
}
return $retval;
} );
return $data;
}
/* example */
$data = array(
array( "firstname" => "Mary", "lastname" => "Johnson", "age" => 25 ),
array( "firstname" => "Amanda", "lastname" => "Miller", "age" => 18 ),
array( "firstname" => "James", "lastname" => "Brown", "age" => 31 ),
array( "firstname" => "Patricia", "lastname" => "Williams", "age" => 7 ),
array( "firstname" => "Michael", "lastname" => "Davis", "age" => 43 ),
array( "firstname" => "Sarah", "lastname" => "Miller", "age" => 24 ),
array( "firstname" => "Patrick", "lastname" => "Miller", "age" => 27 )
);
$data = sortArray( $data, 'age' );
$data = sortArray( $data, array( 'lastname', 'firstname' ) );
Я хотел бы добавить здесь свое собственное решение, поскольку оно предлагает функции, которых нет в других ответах.
В частности, к преимуществам этого решения можно отнести:
DateTime).usort или uasort).array_multisort: хотя array_multisort удобен, он зависит от создания проекции всех ваших входных данных перед сортировкой. Это требует времени и памяти и может быть просто недопустимым, если у вас большой набор данных.function make_comparer() {
// Normalize criteria up front so that the comparer finds everything tidy
$criteria = func_get_args();
foreach ($criteria as $index => $criterion) {
$criteria[$index] = is_array($criterion)
? array_pad($criterion, 3, null)
: array($criterion, SORT_ASC, null);
}
return function($first, $second) use (&$criteria) {
foreach ($criteria as $criterion) {
// How will we compare this round?
list($column, $sortOrder, $projection) = $criterion;
$sortOrder = $sortOrder === SORT_DESC ? -1 : 1;
// If a projection was defined project the values now
if ($projection) {
$lhs = call_user_func($projection, $first[$column]);
$rhs = call_user_func($projection, $second[$column]);
}
else {
$lhs = $first[$column];
$rhs = $second[$column];
}
// Do the actual comparison; do not return if equal
if ($lhs < $rhs) {
return -1 * $sortOrder;
}
else if ($lhs > $rhs) {
return 1 * $sortOrder;
}
}
return 0; // tiebreakers exhausted, so $first == $second
};
}
В этом разделе я буду предоставлять ссылки, которые сортируют этот образец данных:
$data = array(
array('zz', 'name' => 'Jack', 'number' => 22, 'birthday' => '12/03/1980'),
array('xx', 'name' => 'Adam', 'number' => 16, 'birthday' => '01/12/1979'),
array('aa', 'name' => 'Paul', 'number' => 16, 'birthday' => '03/11/1987'),
array('cc', 'name' => 'Helen', 'number' => 44, 'birthday' => '24/06/1967'),
);
Функция make_comparer принимает переменное количество аргументов, определяющих желаемую сортировку, и возвращает функцию, которую вы должны использовать в качестве аргумента для usort или uasort.
Самый простой вариант использования - передать ключ, который вы хотите использовать для сравнения элементов данных. Например, чтобы отсортировать $data по элементу name, вы должны сделать
usort($data, make_comparer('name'));
Ключ также может быть числом, если элементы представляют собой массивы с числовым индексом. Для примера в вопросе это будет
usort($data, make_comparer(0)); // 0 = first numerically indexed column
Вы можете указать несколько столбцов сортировки, передав дополнительные параметры make_comparer. Например, для сортировки по «числу», а затем по столбцу с нулевым индексом:
usort($data, make_comparer('number', 0));
Более продвинутые функции доступны, если вы укажете столбец сортировки как массив вместо простой строки. Этот массив должен быть численно проиндексирован и должен содержать следующие элементы:
0 => the column name to sort on (mandatory)
1 => either SORT_ASC or SORT_DESC (optional)
2 => a projection function (optional)
Давайте посмотрим, как мы можем использовать эти функции.
Чтобы отсортировать по имени по убыванию:
usort($data, make_comparer(['name', SORT_DESC]));
Для сортировки по убыванию номера, а затем по убыванию имени:
usort($data, make_comparer(['number', SORT_DESC], ['name', SORT_DESC]));
В некоторых сценариях может потребоваться сортировка по столбцу, значения которого не подходят для сортировки. Столбец «день рождения» в наборе данных образца соответствует этому описанию: нет смысла сравнивать дни рождения в виде строк (потому что, например, «01.01.1980» предшествует «10.10.1970»). В этом случае мы хотим указать, как проект фактических данных в форму, чтобы может напрямую сравнивался с желаемой семантикой.
Прогнозы могут быть указаны как вызываемый любого типа: как строки, массивы или анонимные функции. Предполагается, что проекция принимает один аргумент и возвращает его спроектированную форму.
Следует отметить, что, хотя проекции аналогичны пользовательским функциям сравнения, используемым в usort и семействе, они проще (вам нужно только преобразовать одно значение в другое) и используют все функции, уже встроенные в make_comparer.
Давайте отсортируем пример набора данных без проекции и посмотрим, что произойдет:
usort($data, make_comparer('birthday'));
Это был не желаемый результат. Но мы можем использовать date_create как проекцию:
usort($data, make_comparer(['birthday', SORT_ASC, 'date_create']));
Это правильный порядок, который мы хотели.
Есть еще много вещей, которых можно достичь с помощью прогнозов. Например, быстрый способ получить сортировку без учета регистра - использовать strtolower в качестве проекции.
Тем не менее, я должен также упомянуть, что лучше не использовать прогнозы, если ваш набор данных велик: в этом случае было бы намного быстрее спроецировать все ваши данные вручную заранее, а затем отсортировать без использования прогноза, хотя это будет торговаться увеличенное использование памяти для более высокой скорости сортировки.
Наконец, вот пример, в котором используются все функции: сначала выполняется сортировка по убыванию числа, а затем по возрастанию дня рождения:
usort($data, make_comparer(
['number', SORT_DESC],
['birthday', SORT_ASC, 'date_create']
));
@Jon Когда я проверяю ваше последнее действие (с обратным вызовом date_create), дни рождения указаны в неправильном порядке. Не могли бы вы подтвердить ? Я получаю 01/12/1979, 03/11/1987, 12/03/1980 и 24/06/1967. Если я использую strtotime, я получаю хороший результат. Я предполагаю, что DateTime сломан.
@ DavidBélanger: Какой именно URL? Все примеры корректно работают как на ideone.com, так и на моем локальном компьютере.
Привет, @Jon, я не знаю, как с тобой связаться, кроме как через комментарий. Я вижу, вы создали тег «многомерный массив», но нам также нужны «многомерные данные» для аналогичного термина в визуализации данных и связанных дисциплинах. Я не могу создавать теги, могу я попросить вас создать их, если вы сочтете это целесообразным?
@VividD: Я не создавал тег сам, и создание тега не может происходить само по себе. Новые теги создаются неявно, когда кто-то с необходимыми привилегиями использует их, чтобы пометить вопрос; учитывая, что я никогда не видел и, вероятно, никогда не увижу вопрос, который, как мне кажется, должен быть помечен таким образом, я ничем не могу помочь.
Не могли бы вы привести пример реализации сортировки без учета регистра? Я попытался преобразовать проекцию в нижний регистр, но ничего не вышло ...
@ Андрей: Пример. Но помните, что это неэффективно, поэтому не делайте этого, если ваш набор данных не мал.
@Jon, спасибо, я только что попробовал, и он на 0,006 секунды медленнее, чем без него, сортировка 200 строк. Думаю, я смирюсь с такой неэффективностью :)
У меня есть еще один массив значений, по которым я хотел бы отсортировать столбец, и значение в столбце может иметь несколько совпадений. Массив сортировки относится к типам файлов, поэтому я всегда хочу, чтобы типы файлов списка файлов отображались в определенном порядке. Возможно ли это в этой конструкции?
@Ecropolis: опубликуйте хороший пример ваших входных данных и желаемого результата где-нибудь (например, ideone.com или pastebin.com), чтобы мы могли устранить недоразумения.
@Jon - Спасибо, вот примеры массивов с комментариями. ideone.com/utbrNt
@Ecropolis: Хорошо, поэтому вы можете создать массив, где ключи - это типы файлов, а значения - "веса" (меньший вес = элемент идет вверху) с помощью $weights = array_flip($sort_array). $weights теперь ['mp4' => 0, 'mpeg' => 1, ...]. Затем вы можете использовать это в компараторе, отсортировав по типу файла и используя это как проекцию: function($t) use ($weights) { return $weights[$t]; }).
Отлично! Спасибо, @Jon. Я обновил ideone.com/utbrNt, чтобы получить полный пример того, как использовать эту функцию для сортировки массива, используя другой для значений ссылочной сортировки.
@Ecropolis: У вас есть лишние цитаты вокруг function($t) ..., которые нужно удалить. Ваше здоровье!
@Jon - Спасибо за вашу помощь. Я попытался реализовать это на своем сервере и получил следующее: Ошибка синтаксического анализа PHP: синтаксическая ошибка, неожиданное '[', ожидание ')' - Я пробовал разные варианты, я просто немного запутался в том, как это работает на самом деле. У меня v5.3.28 - ideone.com запускает php 5.4 - в этом ли проблема?
@Ecropolis: PHP 5.3 не поддерживает короткий синтаксис массива [...], вам придется использовать вместо него array(...). Я не делал этого в примерах для краткости, но сам make_comparer совместим с 5.3.
@Jon Отличный ответ, и я согласен, что это похоже на веб-сайт, а не на ответ. Спасибо. Есть только один вопрос. Как я могу заставить его работать для объектов?
@YahyaE: Сортировка массивов объектов? Замените $first[$column] на $first->$column и то же самое на $second. Всего четыре замены.
у меня 4D-массив? как я могу передать индекс, если это возможно?
@Dashrath: Я не уверен, какая у вас структура. Либо перейдите в $array['index'], если вы хотите избавиться от верхнего уровня, либо используйте проекцию function($sub) { return $sub['index']; }, если вы хотите избавиться от измерения среднего уровня с именем index.
@CalculatingMachine Я прочитал вопрос, но вы не показываете, что пытались сделать. Вроде бы usort($data['content'], get_comparer('price')) было бы достаточно, но я не уверен.
@Jon спасибо за ваш комментарий. Однако я все еще читаю ваш ответ и пытаюсь понять его. При необходимости я буду использовать то же самое в своем проекте. Было одно сомнение. Я не уверен, что он будет работать с моими данными / массивом или нет. В вашем примере данные имеют другой формат.
Я попробовал несколько популярных ответов array_multisort () и usort (), и ни один из них не помог мне. Данные просто перемешиваются, и код нечитаем. Вот быстрое грязное решение. ВНИМАНИЕ: используйте это только в том случае, если вы уверены, что мошенник-разделитель не вернется, чтобы преследовать вас позже!
Допустим, каждая строка в вашем мульти-массиве выглядит так: name, stuff1, stuff2:
// Sort by name, pull the other stuff along for the ride
foreach ($names_stuff as $name_stuff) {
// To sort by stuff1, that would be first in the contatenation
$sorted_names[] = $name_stuff[0] .','. name_stuff[1] .','. $name_stuff[2];
}
sort($sorted_names, SORT_STRING);
Вам нужно вернуть вещи в алфавитном порядке?
foreach ($sorted_names as $sorted_name) {
$name_stuff = explode(',',$sorted_name);
// use your $name_stuff[0]
// use your $name_stuff[1]
// ...
}
Да, это грязно. Но супер просто, не взорвет голову.
Вы можете отсортировать массив с помощью функции усорт.
$array = array(
array('price'=>'1000.50','product'=>'product 1'),
array('price'=>'8800.50','product'=>'product 2'),
array('price'=>'200.0','product'=>'product 3')
);
function cmp($a, $b) {
return $a['price'] > $b['price'];
}
usort($array, "cmp");
print_r($array);
Выход :
Array
(
[0] => Array
(
[price] => 134.50
[product] => product 1
)
[1] => Array
(
[price] => 2033.0
[product] => product 3
)
[2] => Array
(
[price] => 8340.50
[product] => product 2
)
)
(2 года спустя ...) Если вы сортируете даты, хранящиеся в виде строк, вам может сначала потребоваться использовать strtotime [1] docs.php.net/manual/en/function.strtotime.php