本问题已经有最佳答案,请猛点这里访问。
我将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格式的日期信息,我希望能够将最近的日期作为第一行进行排序。
(2年后…)如果要对存储为字符串的日期进行排序,可能首先需要使用strtotime[1]docs.php.net/manual/en/function.strtotime.php
介绍:针对php 5.3的一个非常通用的解决方案+
我想在这里添加我自己的解决方案,因为它提供了其他答案不能提供的功能。好的。
具体来说,此解决方案的优势包括:好的。
它是可重用的:您将排序列指定为变量,而不是硬编码它。
它是灵活的:您可以指定多个排序列(尽可能多),额外的列用作初始比较相等的项之间的分隔符。
它是可逆的:您可以为每一列指定应该反转的排序。
它是可扩展的:如果数据集包含不能以"哑"方式进行比较的列(例如日期字符串),您还可以指定如何将这些项转换为可以直接进行比较的值(例如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的参数。好的。
最简单的用例是传入要用于比较数据项的键。例如,要按name项对$data进行排序,您需要好的。2
行动起来看看。好的。
如果项是数字索引数组,则键也可以是数字。对于问题中的示例,这将是好的。
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和family一起使用的自定义比较函数,但它们更简单(只需将一个值转换为另一个值),并利用已烘焙到make_comparer中的所有功能。好的。
让我们对没有投影的示例数据集进行排序,看看会发生什么:好的。
usort($data, make_comparer('birthday'));
行动起来看看。好的。
这不是预期的结果。但我们可以使用date_create作为投影:好的。
usort($data, make_comparer(['birthday', SORT_ASC, 'date_create']));
行动起来看看。好的。
这是我们想要的正确顺序。好的。
还有很多事情是可以实现的。例如,快速获得不区分大小写排序的方法是使用strtolower作为投影。好的。
这就是说,我还应该提到,如果数据集很大,最好不要使用投影:在这种情况下,提前手动投影所有数据,然后在不使用投影的情况下进行排序要快得多,尽管这样做会牺牲内存使用量的增加来获得更快的排序速度。好的。
最后,下面是一个使用所有特性的示例:它首先按数字降序排序,然后按生日升序排序:好的。2
行动起来看看。好的。好啊。
在这个网站上最容易被低估的答案。
@乔恩,当我检查你最后一次行动时(用回叫date_create时),生日的顺序是错误的。你能证实吗?我得到了01/12/1979、03/11/1987、12/03/1980和24/06/1967。如果我用strtotime代替,我会得到一个很好的结果。我想是日期时间坏了。
@david&233;langer:确切的网址是什么?所有示例都在ideone.com和本地计算机上正常工作。
我来派对有点晚了,不过我想谢谢你。非常有用。
这可能是我在StackOverflow上看到的最佳答案。感谢您漂亮的代码和精彩的文档!!!!
嘿,@jon,我不知道怎么联系你,只是通过评论。我看到您创建了标记"多维数组",但在数据可视化和相关规程中,我们还需要类似术语的"多维数据"。我不能创建标签,如果你觉得有意义的话,我可以请你创建一个吗?
@维维德:我没有自己创建标签,标签的创建不能单独进行。当具有所需权限的人使用新标签来标记问题时,会隐式地创建新标签;考虑到我从未见过并且可能永远也不会看到我认为应该这样标记的问题,我真的帮不上忙。
你能举例说明如何实现不区分大小写的排序吗?我试着把投影转换成小写,但不起作用…
@安德鲁:举个例子。但请记住,这是低效的,所以如果数据集不小,就不要这样做。
@乔恩,谢谢,我刚试过,比没有它慢0.006秒,排序200行。我想我会接受这种低效的做法:)
我有另一个值数组,我想对一列进行排序,该列中的值可以有多个匹配项。排序数组是文件类型,所以我总是希望文件列表中的文件类型按特定顺序显示。在这个结构中是可能的吗?
@Ecropolis:请在某个地方(如ideone.com或pastebin.com)张贴您的输入和期望输出的好例子,以便我们消除误解。
@乔恩-谢谢,这里有带注释的示例数组。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) ...的额外报价需要删除。干杯!
@乔恩-谢谢你的帮助。我尝试在服务器上实现它,得到了这个结果:php解析错误:语法错误,意外的"[",预期的')'——我尝试了不同的变体,我对它的实际工作方式有点迷茫。我在v5.3.28上——ideone.com运行php 5.4——这就是问题所在吗?
@ecropolis:php 5.3不支持简短的[...]数组语法,您将不得不使用array(...)。我在示例中没有这样做,因为它非常简洁,但是make_comparer本身与5.3兼容。
@乔恩回答得很好,我同意这就像一个网站,而不是答案。谢谢。只有一个问题。我怎样才能让它运行对象?
@对物体的排列?用$first->$column代替$first[$column],用$second代替。共更换4个。
我的是4D阵列?如果可能的话,我如何传递索引?
@达什拉:我不知道你的结构是什么。如果你想去掉顶层,要么传入$array['index'],要么使用投影function($sub) { return $sub['index']; },如果你想去掉名为index的中层维。
@jon请帮我解决这个问题stackoverflow.com/questions/32775857/&hellip;提前谢谢
@乔恩感谢你的回答。但是,我发现它很难使用。你能在stackoverflow.com/questions/36784955/hellip上给我提个建议吗?
@计算机器我读过这个问题,但你没有表现出你想做什么。这似乎就足够了,但我不能确定。
@乔恩,谢谢你的评论。然而,我仍然在阅读你的答案并试图理解它。如果需要,我会在我的项目中使用相同的方法。只是有一个疑问。我不确定它是否适用于我的数据/数组。在示例中,数据的格式不同。
您可以使用array_multisort()。
尝试如下操作:2
对于php>=5.5.0,只需提取要排序的列。不需要循环:
array_multisort(array_column($mdarray, 0), SORT_DESC, $mdarray);
我想这说明了为什么我以前不能让阵列多端口工作。
array_multisort()是我一直以来的工作方式,不过一开始很难把你的头放在工作方式上。
所以在这个例子中,$mdarray可能是一个二维数组,就像一个数据库记录数组。在本例中,0是每个记录(或行)中"日期"列的索引。因此,您构造$dates数组(基本上是相同的数组,但只有该列),并告诉数组的multisort函数根据特定列的值对$mdarray进行排序。
为了清楚起见,您可以在这个示例的开头添加$dates = array();。
这个答案对我目前的项目真的很有帮助,谢谢!
数组多端口是否应与关联数组一起工作(将$row[0]更改为$row['whatever']?不要去这里。在我将数组改为数字后,函数按预期工作。
在使用array_multisort()时,是否不需要包含$key?写foreach ($mdarray as $row) { $sortByDate[] = $row['date']; }比array_multisort( $sortByDate, SORT_DESC, $mdarray );更简单,更有意(你的语义里程我的变化)。
如果答案是array_multi_sort(),那么这个问题就不被理解了。虽然在技术上可行,但通常有一个更好的解决方案,它使用用户生成的比较函数和usort()函数。保养起来比较容易。使用multisert,您通常会创建代码来准备数据进行排序。如果数据结构发生更改,则可能会丢弃该代码。使用usort(),您可以更改比较函数-与更改数据结构的方式相同。
现在在php array_multisort页面上有一个更广泛的例子,这个答案是php.net/manual/en/function.array multisort.php example-4928。
不知什么原因,usort没有为我工作。确实如此。
警告:array_multisort():数组大小不一致
答案很好。它对我很有用,
随附。下面是一个通用的解决方案,可以用于不同的列:
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_字符串,在该类的第二行需要t_old_函数或t_函数或t_var或''。
此代码需要php5
将"protected"替换为"var","uuuu construct"替换为"tablesorter",它将在php4中工作。但请注意,php4已停止使用。
我将php设置为v5,但不知道它在默认情况下运行v4。看了一会儿,我想我也知道如何修改它以适应不同的类型。
使用闭包进行多行排序
下面是另一种使用uasort()和匿名回调函数(closure)的方法。我经常使用这个功能。需要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' ) );
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-array-by-value-of-given-key-in-php/
我知道这个问题已经问了2年了,但这里还有一个对二维数组排序的函数。它接受可变数量的参数,允许您传入多个要排序的键(即列名)。需要php 5.3。
function sort_multi_array ($array, $key)
{
$keys = array();
for ($i=1;$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
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);替换?
是的,我想是的
"usort"函数就是您的答案。http://php.net/us端口
我对你投了反对票,因为你未能提供一个解决方案,对最初的问题进行解释和举例。更新你的回复,我将撤销我的投票。
下面是将对一个或多个字段进行排序的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'));
这只适用于关联数组吗?
是-仅关联数组。现在我来看一下,这不是解决这个问题的正确方法。
我尝试了几个流行的数组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]
// ...
}
是的,很脏。但超级简单,不会让你的头爆炸。
在我能够运行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是要排序的MD数组。
$column是要排序的列。
$method是执行排序的方式,例如排序描述
如果第一行包含不希望排序的标题值,$has_header设置为true。
我更喜欢使用阵列多端口。参见文档在这里。