如何在 PHP 中对多维数组进行排序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/96759/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 21:26:12  来源:igfitidea点击:

How do I Sort a Multidimensional Array in PHP

phpsortingmultidimensional-array

提问by Melikoth

I have CSV data loaded into a multidimensional array. In this way each "row" is a record and each "column" contains the same type of data. I am using the function below to load my CSV file.

我已将 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;
}

I need to be able to specify a column to sort so that it rearranges the rows. One of the columns contains date information in the format of Y-m-d H:i:sand I would like to be able to sort with the most recent date being the first row.

我需要能够指定一列进行排序,以便它重新排列行。其中一列包含格式为的日期信息,Y-m-d H:i:s我希望能够将最近的日期作为第一行进行排序。

回答by Jon

Introducing: a very generalized solution for PHP 5.3+

介绍:一个非常通用的 PHP 5.3+ 解决方案

I 'd like to add my own solution here, since it offers features that other answers do not.

我想在这里添加我自己的解决方案,因为它提供了其他答案所没有的功能。

Specifically, advantages of this solution include:

具体来说,该解决方案的优势包括:

  1. It's reusable: you specify the sort column as a variable instead of hardcoding it.
  2. It's flexible: you can specify multiple sort columns (as many as you want) -- additional columns are used as tiebreakers between items that initially compare equal.
  3. It's reversible: you can specify that the sort should be reversed -- individually for each column.
  4. It's extensible: if the data set contains columns that cannot be compared in a "dumb" manner (e.g. date strings) you can also specify how to convert these items to a value that can be directly compared (e.g. a DateTimeinstance).
  5. It's associative if you want: this code takes care of sorting items, but youselect the actual sort function (usortor uasort).
  6. Finally, it does not use array_multisort: while array_multisortis convenient, it depends on creating a projection of all your input data before sorting. This consumes time and memory and may be simply prohibitive if your data set is large.
  1. 它是可重用的:您将排序列指定为变量而不是对其进行硬编码。
  2. 它很灵活:您可以指定多个排序列(任意数量)——额外的列用作最初比较相等的项目之间的决胜局。
  3. 它是可逆的:您可以指定应该反转排序——针对每一列单独进行排序。
  4. 它是可扩展的:如果数据集包含无法以“哑”方式进行比较的列(例如日期字符串),您还可以指定如何将这些项目转换为可以直接比较的值(例如DateTime实例)。
  5. 如果需要,它是关联的:此代码负责对项目进行排序,但选择实际的排序函数(usortuasort)。
  6. 最后,它不使用array_multisort: whilearray_multisort很方便,它取决于在排序之前创建所有输入数据的投影。这会消耗时间和内存,如果您的数据集很大,可能会令人望而却步。

The code

编码

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
    };
}

How to use

如何使用

Throughout this section I will provide links that sort this sample data set:

在本节中,我将提供对这个示例数据集进行排序的链接:

$data = array(
    array('zz', 'name' => 'Hyman', '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'),
);

The basics

基础知识

The function make_compareraccepts a variable number of arguments that define the desired sort and returns a function that you are supposed to use as the argument to usortor uasort.

该函数make_comparer接受定义所需排序的可变数量的参数,并返回一个您应该用作usortor参数的函数uasort

The simplest use case is to pass in the key that you 'd like to use to compare data items. For example, to sort $databy the nameitem you would do

最简单的用例是传入要用于比较数据项的键。例如,要按您要执行$dataname项目排序

usort($data, make_comparer('name'));

See it in action.

看到它在行动

The key can also be a number if the items are numerically indexed arrays. For the example in the question, this would be

如果项目是数字索引数组,则键也可以是数字。对于问题中的示例,这将是

usort($data, make_comparer(0)); // 0 = first numerically indexed column

See it in action.

看到它在行动

Multiple sort columns

多个排序列

You can specify multiple sort columns by passing additional parameters to make_comparer. For example, to sort by "number" and then by the zero-indexed column:

您可以通过将附加参数传递给 来指定多个排序列make_comparer。例如,要按“数字”排序,然后按零索引列排序:

usort($data, make_comparer('number', 0));

See it in action.

看到它在行动

Advanced features

高级功能

More advanced features are available if you specify a sort column as an array instead of a simple string. This array should be numerically indexed, and must contain these items:

如果您将排序列指定为数组而不是简单的字符串,则可以使用更高级的功能。这个数组应该有数字索引,并且必须包含以下项目:

0 => the column name to sort on (mandatory)
1 => either SORT_ASC or SORT_DESC (optional)
2 => a projection function (optional)

Let's see how we can use these features.

让我们看看如何使用这些功能。

Reverse sort

反向排序

To sort by name descending:

要按名称降序排序:

usort($data, make_comparer(['name', SORT_DESC]));

See it in action.

看到它在行动

To sort by number descending and then by name descending:

要按数字降序排序,然后按名称降序排序:

usort($data, make_comparer(['number', SORT_DESC], ['name', SORT_DESC]));

See it in action.

看到它在行动

Custom projections

自定义投影

In some scenarios you may need to sort by a column whose values do not lend well to sorting. The "birthday" column in the sample data set fits this description: it does not make sense to compare birthdays as strings (because e.g. "01/01/1980" comes before "10/10/1970"). In this case we want to specify how to projectthe actual data to a form that canbe compared directly with the desired semantics.

在某些情况下,您可能需要按值不适合排序的列进行排序。样本数据集中的“生日”列符合这种描述:将生日作为字符串进行比较是没有意义的(因为例如“01/01/1980”在“10/10/1970”之前)。在这种情况下,我们要指定如何将实际数据投影可以直接与所需语义进行比较的形式。

Projections can be specified as any type of callable: as strings, arrays, or anonymous functions. A projection is assumed to accept one argument and return its projected form.

投影可以指定为任何类型的可调用对象:字符串、数组或匿名函数。假设投影接受一个参数并返回其投影形式。

It should be noted that while projections are similar to the custom comparison functions used with usortand family, they are simpler (you only need to convert one value to another) and take advantage of all the functionality already baked into make_comparer.

应该注意的是,虽然投影类似于用于usort和 系列的自定义比较函数,但它们更简单(您只需将一个值转换为另一个值)并利用make_comparer.

Let's sort the example data set without a projection and see what happens:

让我们对没有投影的示例数据集进行排序,看看会发生什么:

usort($data, make_comparer('birthday'));

See it in action.

看到它在行动

That was not the desired outcome. But we can use date_createas a projection:

那不是想要的结果。但我们可以date_create用作投影:

usort($data, make_comparer(['birthday', SORT_ASC, 'date_create']));

See it in action.

看到它在行动

This is the correct order that we wanted.

这是我们想要的正确顺序。

There are many more things that projections can achieve. For example, a quick way to get a case-insensitive sort is to use strtoloweras a projection.

投影可以实现的目标还有很多。例如,获得不区分大小写的排序的一种快速方法是strtolower用作投影。

That said, I should also mention that it's better to not use projections if your data set is large: in that case it would be much faster to project all your data manually up front and then sort without using a projection, although doing so will trade increased memory usage for faster sort speed.

也就是说,我还应该提到,如果您的数据集很大,最好不要使用投影:在这种情况下,预先手动投影所有数据然后不使用投影进行排序会快得多,尽管这样做会交易增加内存使用以加快排序速度。

Finally, here is an example that uses all the features: it first sorts by number descending, then by birthday ascending:

最后,这是一个使用所有功能的示例:它首先按数字降序排序,然后按生日升序排序:

usort($data, make_comparer(
    ['number', SORT_DESC],
    ['birthday', SORT_ASC, 'date_create']
));

See it in action.

看到它在行动

回答by Shinhan

You can use array_multisort()

您可以使用array_multisort()

Try something like this:

尝试这样的事情:

foreach ($mdarray as $key => $row) {
    // replace 0 with the field's index/key
    $dates[$key]  = $row[0];
}

array_multisort($dates, SORT_DESC, $mdarray);

For PHP >= 5.5.0 just extract the column to sort by. No need for the loop:

对于 PHP >= 5.5.0 只需提取要排序的列。不需要循环:

array_multisort(array_column($mdarray, 0), SORT_DESC, $mdarray);

回答by troelskn

With usort. Here's a generic solution, that you can use for different columns:

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;
  }
}

To sort by first column:

按第一列排序:

$sorter = new TableSorter(0); // sort by first column
$mdarray = $sorter->sort($mdarray);

回答by feeela

Multiple row sorting using a closure

使用闭包进行多行排序

Here's another approach using uasort() and an anonymous callback function (closure). I've used that function regularly. PHP 5.3 required– no more dependencies!

这是使用 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' ) );

回答by Mike C

I know it's 2 years since this question was asked and answered, but here's another function that sorts a two-dimensional array. It accepts a variable number of arguments, allowing you to pass in more than one key (ie column name) to sort by. PHP 5.3 required.

我知道这个问题被提出和回答已经过去了 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;
}

Try it here: http://www.exorithm.com/algorithm/view/sort_multi_array

在这里试试:http: //www.exorithm.com/algorithm/view/sort_multi_array

回答by Kamal

function cmp($a, $b)
{
$p1 = $a['price'];
$p2 = $b['price'];
return (float)$p1 > (float)$p2;
}
uasort($my_array, "cmp");

http://qaify.com/sort-an-array-of-associative-arrays-by-value-of-given-key-in-php/

http://qaify.com/sort-an-array-of-associative-arrays-by-value-of-given-key-in-php/

回答by Devon

Here is a php4/php5 class that will sort one or more fields:

这是一个 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'));

回答by Jan Han?i?

The "Usort" function is your answer.
http://php.net/usort

“Usort”函数就是你的答案。
http://php.net/usort

回答by Melikoth

Before I could get the TableSorter class to run I had came up with a function based on what Shinhanhad provided.

在让 TableSorter 类运行之前,我想出了一个基于Shinhan提供的函数。

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;
  }
  • $array is the MD Array you want to sort.
  • $column is the column you wish to sort by.
  • $method is how you want the sort performed, such as SORT_DESC
  • $has_header is set to true if the first row contains header values that you don't want sorted.
  • $array 是您要排序的 MD 数组。
  • $column 是您希望排序的列。
  • $method 是您希望如何执行排序,例如 SORT_DESC
  • 如果第一行包含您不想排序的标题值,则 $has_header 设置为 true。

回答by PJ Brunet

I tried several popular array_multisort() and usort() answers and none of them worked for me. The data just gets jumbled and the code is unreadable. Here's a quick a dirty solution. WARNING: Only use this if you're sure a rogue delimiter won't come back to haunt you later!

我尝试了几个流行的 array_multisort() 和 usort() 答案,但没有一个对我有用。数据变得混乱,代码不可读。这是一个快速肮脏的解决方案。警告:只有在您确定流氓分隔符以后不会回来困扰您时才使用它!

Let's say each row in your multi array looks like: name, stuff1, stuff2:

假设多数组中的每一行看起来像: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);

Need your stuff back in alphabetical order?

需要按字母顺序放回您的东西吗?

foreach ($sorted_names as $sorted_name) {
    $name_stuff = explode(',',$sorted_name);
    // use your $name_stuff[0] 
    // use your $name_stuff[1] 
    // ... 
}

Yeah, it's dirty. But super easy, won't make your head explode.

是的,很脏。但超级简单,不会让你的头爆炸。