There are numerous questions here asking how to sort a multi-dimensional array in PHP. The answer is usort()
. I know that. But I have a question that takes it a bit further, and I couldn't see a similar answer here.
这里有很多问题,询问如何在PHP中对多维数组进行排序。答案是usort()。我知道。但是我有一个问题让它更进一步,我在这里看不到类似的答案。
I have an array of records, each of which includes a country ID (or a country name if you prefer; it's not relevant).
我有一系列记录,每个记录都包含一个国家ID(如果您愿意,可以包含国家/地区名称;它不相关)。
My task is to sort the array in such a way as to favour certain countries. This is dynamic -- ie the choice of countries to favour is determined by the user's config. I have a separate array which specifies the required sort order for the first few countries; results from other countries would just be left unsorted at the end of the list.
我的任务是以有利于某些国家的方式对阵列进行排序。这是动态的 - 即支持的国家的选择取决于用户的配置。我有一个单独的数组,它指定了前几个国家所需的排序顺序;来自其他国家的结果只会在列表末尾未分类。
So the question is: how do I get the this sort criteria into usort()
without resorting to using a global variable. And preferably without injecting the criteria array into every element of the main array ('coz if I'm going to loop it anyway, what's the point in using usort()
at all?)
所以问题是:如何在不使用全局变量的情况下将此排序条件放入usort()中。并且最好不要将条件数组注入到主数组的每个元素中('如果我还要循环它,那么,使用usort()的重点是什么?)
Please note: Since it's going to be relevant to the answers here, I'm stuck on PHP 5.2 for the time being, so I can't use an anonymous function. We are in the process of upgrading, but for now I need answers that will work for 5.2. (answers for 5.3/5.4 will be welcome too, especially if they make it significantly easier, but I won't be able to use them)
请注意:由于它与这里的答案相关,我暂时停留在PHP 5.2上,所以我不能使用匿名函数。我们正在升级,但是现在我需要能够解决5.2的问题。 (5.3 / 5.4的答案也会受到欢迎,特别是如果它们使它变得更容易,但我将无法使用它们)
6 个解决方案
#1
5
You explicitly write that you do not want to have global variables, so I do not make you a suggestion with static variables as well because those are actually global variables - and those are not needed at all.
你明确地写道,你不想拥有全局变量,所以我不会给你一个静态变量的建议,因为那些实际上是全局变量 - 根本不需要那些变量。
In PHP 5.2 (and earlier) if you need call context within the callback, you can create your context by making use of a class of it's own that carries it:
在PHP 5.2(及更早版本)中,如果您需要回调中的调用上下文,您可以通过使用它自己的类来创建您的上下文:
class CallContext
{
}
For example you have the compare function for sort
:
例如,您有sort的比较函数:
class CallContext
{
...
public function compare($a, $b)
{
return $this->weight($a) - $this->weight($b);
}
public function getCallback()
{
return array($this, 'compare');
}
...
}
That function can be used as the following as a callback with usort
then:
该函数可以用作以下的回调,然后使用usort:
$context = new CallContext();
usort($array, $context->getCallback());
Pretty straight forward. The private implementation of CallContext::weight
is still missing, and from your question we know it needs some sort data and information. For example the name of the key of the country id in each record. Lets assume records are Stdclass objects so to get the weight of one record the context class needs to know the name of the property, the sort-order you define your own and a sort-value for those country-ids that are not defined in the custom sort order (the others, the rest).
很直接。 CallContext :: weight的私有实现仍然缺失,从您的问题我们知道它需要一些排序数据和信息。例如,每条记录中国家/地区ID的密钥名称。假设记录是Stdclass对象,因此要获得一个记录的权重,上下文类需要知道属性的名称,您自己定义的排序顺序以及未在其中定义的那些country-id的排序值。自定义排序顺序(其他,其余)。
These configuration values are given with the constructor function (ctor in short) and are stored as private members. The missing weight
function then converts a record into the sort-value based on that information:
这些配置值使用构造函数(简称ctor)给出,并存储为私有成员。缺失的权重函数然后根据该信息将记录转换为排序值:
class CallContext
{
private $property, $sortOrder, $sortOther;
public function __construct($property, $sortOrder, $sortOther = 9999)
{
$this->property = $property;
$this->sortOrder = $sortOrder;
$this->sortOther = $sortOther;
}
private function weight($object) {
if (!is_object($object)) {
throw new InvalidArgumentException(sprintf('Not an object: %s.', print_r($object, 1)));
}
if (!isset($object->{$this->property})) {
throw new InvalidArgumentException(sprintf('Property "%s" not found in object: %s.', $this->property, print_r($object, 1)));
}
$value = $object->{$this->property};
return isset($this->sortOrder[$value])
? $this->sortOrder[$value]
: $this->sortOther;
}
...
The usage now extends to the following:
用法现在扩展到以下内容:
$property = 'country';
$order = array(
# country ID => sort key (lower is first)
46 => 1,
45 => 2
);
$context = new CallContext('country', $order);
usort($array, $context->getCallback());
With the same principle you can very often convert any PHP 5.3 closure with use
clauses to PHP 5.2. The variables from the use
clause become private members injected with construction.
使用相同的原则,您可以经常将带有use子句的任何PHP 5.3闭包转换为PHP 5.2。 use子句中的变量成为注入构造的私有成员。
This variant does not only prevent the usage of static, it also makes visible that you have got some mapping per each element and as both elements are treated equal, it makes use of a private implementation of some weight
function which works very well with usort
.
这个变体不仅可以防止静态的使用,而且还可以看出每个元素都有一些映射,并且因为两个元素都被视为相等,所以它使用了一些权重函数的私有实现,它对usort很有效。
I hope this is helpful.
我希望这是有帮助的。
#2
2
You might not want a global variable, but you need something that behaves like one. You could use a class with static methods and parameters. It won't pollute the global scope that much and it would still function the way you need it.
您可能不需要全局变量,但需要一些行为类似的变量。您可以使用具有静态方法和参数的类。它不会对全球范围造成太大污染,它仍然可以按照您的需要运行。
class CountryCompare {
public static $country_priorities;
public static function compare( $a, $b ) {
// Some custom sorting criteria
// Work with self::country_priorities
}
public static function sort( $countries ) {
return usort( $countries, array( 'CountryCompare', 'compare' ) );
}
}
Then just call it like this:
然后就像这样调用它:
CountryCompare::country_priorities = loadFromConfig();
CountryCompare::sort( $countries );
#3
2
You can use closures (PHP >= 5.3):
你可以使用闭包(PHP> = 5.3):
$weights = array( ... );
usort($records, function($a, $b) use ($weights) {
// use $weights in here as usual and perform your sort logic
});
#4
1
See Demo : http://codepad.org/vDI2k4n6
请参阅演示:http://codepad.org/vDI2k4n6
$arrayMonths = array(
'jan' => array(1, 8, 5,4),
'feb' => array(10,12,15,11),
'mar' => array(12, 7, 4, 3),
'apr' => array(10,16,7,17),
);
$position = array("Foo1","Foo2","Foo3","FooN");
$set = array();
foreach($arrayMonths as $key => $value)
{
$max = max($value);
$pos = array_search($max, $value);
$set[$key][$position[$pos]] = $max ;
}
function cmp($a, $b)
{
foreach($a as $key => $value )
{
foreach ($b as $bKey => $bValue)
{
return $bValue - $value ;
}
}
}
uasort($set,"cmp");
var_dump($set);
output
产量
array
'apr' =>
array
'FooN' => int 17
'feb' =>
array
'Foo3' => int 15
'mar' =>
array
'Foo1' => int 12
'jan' =>
array
'Foo2' => int 8
another example:-
另一个例子:-
Sorting a Multi-Dimensional Array with PHP
使用PHP对多维数组进行排序
http://www.firsttube.com/read/sorting-a-multi-dimensional-array-with-php/
http://www.firsttube.com/read/sorting-a-multi-dimensional-array-with-php/
Every so often I find myself with a multidimensional array that I want to sort by a value in a sub-array. I have an array that might look like this:
我经常发现自己有一个多维数组,我希望按子数组中的值排序。我有一个可能看起来像这样的数组:
//an array of some songs I like
$songs = array(
'1' => array('artist'=>'The Smashing Pumpkins', 'songname'=>'Soma'),
'2' => array('artist'=>'The Decemberists', 'songname'=>'The Island'),
'3' => array('artist'=>'Fleetwood Mac', 'songname' =>'Second-hand News')
);
The problem is thus: I’d like to echo out the songs I like in the format “Songname (Artist),” and I’d like to do it alphabetically by artist. PHP provides many functions for sorting arrays, but none will work here. ksort() will allow me to sort by key, but the keys in the $songs array are irrelevant. asort() allows me to sort and preserves keys, but it will sort $songs by the value of each element, which is also useless, since the value of each is “array()”. usort() is another possible candidate and can do multi-dimensional sorting, but it involves building a callback function and is often pretty long-winded. Even the examples in the PHP docs references specific keys.
因此问题是:我想以“歌曲名(艺术家)”的格式回应我喜欢的歌曲,我想按艺术家的字母顺序进行。 PHP提供了许多用于排序数组的函数,但没有一个可以在这里工作ksort()允许我按键排序,但$ songs数组中的键是无关紧要的。 asort()允许我对键进行排序和保留,但它会按每个元素的值对$ songs进行排序,这也是无用的,因为每个元素的值都是“array()”。 usort()是另一个可能的候选者,可以进行多维排序,但它涉及构建一个回调函数,而且通常很冗长。甚至PHP文档中的示例也引用了特定的键。
So I developed a quick function to sort by the value of a key in a sub-array. Please note this version does a case-insensitive sort. See subval_sort() below.
所以我开发了一个快速函数来按子数组中的键值进行排序。请注意,此版本不区分大小写。请参阅下面的subval_sort()。
function subval_sort($a,$subkey) {
foreach($a as $k=>$v) {
$b[$k] = strtolower($v[$subkey]);
}
asort($b);
foreach($b as $key=>$val) {
$c[] = $a[$key];
}
return $c;
}
To use it on the above, I would simply type:
要在上面使用它,我只需键入:
$songs = subval_sort($songs,'artist');
print_r($songs);
This is what you should expect see:
这是你应该期待的:
Array
(
[0] => Array
(
[artist] => Fleetwood Mac
[song] => Second-hand News
)
[1] => Array
(
[artist] => The Decemberists
[song] => The Island
)
[2] => Array
(
[artist] => The Smashing Pumpkins
[song] => Cherub Rock
)
)
The songs, sorted by artist.
歌曲,按艺术家排序。
#5
0
The answer to your question is indeed in the usort()
function. However, what you need to do is write the function that you pass to it is doing the weighting for you properly.
您的问题的答案确实在usort()函数中。但是,您需要做的是编写传递给它的函数正在为您进行加权。
Most of the time, you have something like
大多数时候,你有类似的东西
if($a>$b)
{
return $a;
}
But what you need to do is something along the lines of
但是你需要做的就是有所作为
if($a>$b || $someCountryID != 36)
{
return $a;
}
else
{
return $b;
}
#6
0
You need to use ksort to sort by weight, not usort. That will be much cleaner.
您需要使用ksort按重量排序,而不是按重量排序。那会更清洁。
Arrange your data in an associative array $weighted_data
in the format weight => country_data_struct
. This is a very intuitive form of presentation for weighted data. Then run
将数据排列在关联数组$ weighted_data中,格式为weight => country_data_struct。这是一种非常直观的加权数据表示形式。然后跑
krsort($weighted_data)
krsort($ weighted_data)
#1
5
You explicitly write that you do not want to have global variables, so I do not make you a suggestion with static variables as well because those are actually global variables - and those are not needed at all.
你明确地写道,你不想拥有全局变量,所以我不会给你一个静态变量的建议,因为那些实际上是全局变量 - 根本不需要那些变量。
In PHP 5.2 (and earlier) if you need call context within the callback, you can create your context by making use of a class of it's own that carries it:
在PHP 5.2(及更早版本)中,如果您需要回调中的调用上下文,您可以通过使用它自己的类来创建您的上下文:
class CallContext
{
}
For example you have the compare function for sort
:
例如,您有sort的比较函数:
class CallContext
{
...
public function compare($a, $b)
{
return $this->weight($a) - $this->weight($b);
}
public function getCallback()
{
return array($this, 'compare');
}
...
}
That function can be used as the following as a callback with usort
then:
该函数可以用作以下的回调,然后使用usort:
$context = new CallContext();
usort($array, $context->getCallback());
Pretty straight forward. The private implementation of CallContext::weight
is still missing, and from your question we know it needs some sort data and information. For example the name of the key of the country id in each record. Lets assume records are Stdclass objects so to get the weight of one record the context class needs to know the name of the property, the sort-order you define your own and a sort-value for those country-ids that are not defined in the custom sort order (the others, the rest).
很直接。 CallContext :: weight的私有实现仍然缺失,从您的问题我们知道它需要一些排序数据和信息。例如,每条记录中国家/地区ID的密钥名称。假设记录是Stdclass对象,因此要获得一个记录的权重,上下文类需要知道属性的名称,您自己定义的排序顺序以及未在其中定义的那些country-id的排序值。自定义排序顺序(其他,其余)。
These configuration values are given with the constructor function (ctor in short) and are stored as private members. The missing weight
function then converts a record into the sort-value based on that information:
这些配置值使用构造函数(简称ctor)给出,并存储为私有成员。缺失的权重函数然后根据该信息将记录转换为排序值:
class CallContext
{
private $property, $sortOrder, $sortOther;
public function __construct($property, $sortOrder, $sortOther = 9999)
{
$this->property = $property;
$this->sortOrder = $sortOrder;
$this->sortOther = $sortOther;
}
private function weight($object) {
if (!is_object($object)) {
throw new InvalidArgumentException(sprintf('Not an object: %s.', print_r($object, 1)));
}
if (!isset($object->{$this->property})) {
throw new InvalidArgumentException(sprintf('Property "%s" not found in object: %s.', $this->property, print_r($object, 1)));
}
$value = $object->{$this->property};
return isset($this->sortOrder[$value])
? $this->sortOrder[$value]
: $this->sortOther;
}
...
The usage now extends to the following:
用法现在扩展到以下内容:
$property = 'country';
$order = array(
# country ID => sort key (lower is first)
46 => 1,
45 => 2
);
$context = new CallContext('country', $order);
usort($array, $context->getCallback());
With the same principle you can very often convert any PHP 5.3 closure with use
clauses to PHP 5.2. The variables from the use
clause become private members injected with construction.
使用相同的原则,您可以经常将带有use子句的任何PHP 5.3闭包转换为PHP 5.2。 use子句中的变量成为注入构造的私有成员。
This variant does not only prevent the usage of static, it also makes visible that you have got some mapping per each element and as both elements are treated equal, it makes use of a private implementation of some weight
function which works very well with usort
.
这个变体不仅可以防止静态的使用,而且还可以看出每个元素都有一些映射,并且因为两个元素都被视为相等,所以它使用了一些权重函数的私有实现,它对usort很有效。
I hope this is helpful.
我希望这是有帮助的。
#2
2
You might not want a global variable, but you need something that behaves like one. You could use a class with static methods and parameters. It won't pollute the global scope that much and it would still function the way you need it.
您可能不需要全局变量,但需要一些行为类似的变量。您可以使用具有静态方法和参数的类。它不会对全球范围造成太大污染,它仍然可以按照您的需要运行。
class CountryCompare {
public static $country_priorities;
public static function compare( $a, $b ) {
// Some custom sorting criteria
// Work with self::country_priorities
}
public static function sort( $countries ) {
return usort( $countries, array( 'CountryCompare', 'compare' ) );
}
}
Then just call it like this:
然后就像这样调用它:
CountryCompare::country_priorities = loadFromConfig();
CountryCompare::sort( $countries );
#3
2
You can use closures (PHP >= 5.3):
你可以使用闭包(PHP> = 5.3):
$weights = array( ... );
usort($records, function($a, $b) use ($weights) {
// use $weights in here as usual and perform your sort logic
});
#4
1
See Demo : http://codepad.org/vDI2k4n6
请参阅演示:http://codepad.org/vDI2k4n6
$arrayMonths = array(
'jan' => array(1, 8, 5,4),
'feb' => array(10,12,15,11),
'mar' => array(12, 7, 4, 3),
'apr' => array(10,16,7,17),
);
$position = array("Foo1","Foo2","Foo3","FooN");
$set = array();
foreach($arrayMonths as $key => $value)
{
$max = max($value);
$pos = array_search($max, $value);
$set[$key][$position[$pos]] = $max ;
}
function cmp($a, $b)
{
foreach($a as $key => $value )
{
foreach ($b as $bKey => $bValue)
{
return $bValue - $value ;
}
}
}
uasort($set,"cmp");
var_dump($set);
output
产量
array
'apr' =>
array
'FooN' => int 17
'feb' =>
array
'Foo3' => int 15
'mar' =>
array
'Foo1' => int 12
'jan' =>
array
'Foo2' => int 8
another example:-
另一个例子:-
Sorting a Multi-Dimensional Array with PHP
使用PHP对多维数组进行排序
http://www.firsttube.com/read/sorting-a-multi-dimensional-array-with-php/
http://www.firsttube.com/read/sorting-a-multi-dimensional-array-with-php/
Every so often I find myself with a multidimensional array that I want to sort by a value in a sub-array. I have an array that might look like this:
我经常发现自己有一个多维数组,我希望按子数组中的值排序。我有一个可能看起来像这样的数组:
//an array of some songs I like
$songs = array(
'1' => array('artist'=>'The Smashing Pumpkins', 'songname'=>'Soma'),
'2' => array('artist'=>'The Decemberists', 'songname'=>'The Island'),
'3' => array('artist'=>'Fleetwood Mac', 'songname' =>'Second-hand News')
);
The problem is thus: I’d like to echo out the songs I like in the format “Songname (Artist),” and I’d like to do it alphabetically by artist. PHP provides many functions for sorting arrays, but none will work here. ksort() will allow me to sort by key, but the keys in the $songs array are irrelevant. asort() allows me to sort and preserves keys, but it will sort $songs by the value of each element, which is also useless, since the value of each is “array()”. usort() is another possible candidate and can do multi-dimensional sorting, but it involves building a callback function and is often pretty long-winded. Even the examples in the PHP docs references specific keys.
因此问题是:我想以“歌曲名(艺术家)”的格式回应我喜欢的歌曲,我想按艺术家的字母顺序进行。 PHP提供了许多用于排序数组的函数,但没有一个可以在这里工作ksort()允许我按键排序,但$ songs数组中的键是无关紧要的。 asort()允许我对键进行排序和保留,但它会按每个元素的值对$ songs进行排序,这也是无用的,因为每个元素的值都是“array()”。 usort()是另一个可能的候选者,可以进行多维排序,但它涉及构建一个回调函数,而且通常很冗长。甚至PHP文档中的示例也引用了特定的键。
So I developed a quick function to sort by the value of a key in a sub-array. Please note this version does a case-insensitive sort. See subval_sort() below.
所以我开发了一个快速函数来按子数组中的键值进行排序。请注意,此版本不区分大小写。请参阅下面的subval_sort()。
function subval_sort($a,$subkey) {
foreach($a as $k=>$v) {
$b[$k] = strtolower($v[$subkey]);
}
asort($b);
foreach($b as $key=>$val) {
$c[] = $a[$key];
}
return $c;
}
To use it on the above, I would simply type:
要在上面使用它,我只需键入:
$songs = subval_sort($songs,'artist');
print_r($songs);
This is what you should expect see:
这是你应该期待的:
Array
(
[0] => Array
(
[artist] => Fleetwood Mac
[song] => Second-hand News
)
[1] => Array
(
[artist] => The Decemberists
[song] => The Island
)
[2] => Array
(
[artist] => The Smashing Pumpkins
[song] => Cherub Rock
)
)
The songs, sorted by artist.
歌曲,按艺术家排序。
#5
0
The answer to your question is indeed in the usort()
function. However, what you need to do is write the function that you pass to it is doing the weighting for you properly.
您的问题的答案确实在usort()函数中。但是,您需要做的是编写传递给它的函数正在为您进行加权。
Most of the time, you have something like
大多数时候,你有类似的东西
if($a>$b)
{
return $a;
}
But what you need to do is something along the lines of
但是你需要做的就是有所作为
if($a>$b || $someCountryID != 36)
{
return $a;
}
else
{
return $b;
}
#6
0
You need to use ksort to sort by weight, not usort. That will be much cleaner.
您需要使用ksort按重量排序,而不是按重量排序。那会更清洁。
Arrange your data in an associative array $weighted_data
in the format weight => country_data_struct
. This is a very intuitive form of presentation for weighted data. Then run
将数据排列在关联数组$ weighted_data中,格式为weight => country_data_struct。这是一种非常直观的加权数据表示形式。然后跑
krsort($weighted_data)
krsort($ weighted_data)