7

这里有很多问题询问如何在 PHP 中对多维数组进行排序。答案是usort()。我知道。但我有一个问题更进一步,我在这里看不到类似的答案。

我有一系列记录,每条记录都包含一个国家 ID(或国家名称,如果您愿意,它不相关)。

我的任务是以有利于某些国家的方式对数组进行排序。这是动态的——也就是说,偏好的国家/地区的选择是由用户的配置决定的。我有一个单独的数组,它指定前几个国家所需的排序顺序;来自其他国家的结果将在列表末尾未排序。

所以问题是:如何在usort()不使用全局变量的情况下获得这种排序标准。最好不要将标准数组注入主数组的每个元素('因为如果我无论如何都要循环它,那么使用usort()有什么意义?)

请注意:由于它将与此处的答案相关,因此我暂时停留在 PHP 5.2 上,因此无法使用匿名函数。我们正在升级,但现在我需要适用于 5.2 的答案。(也欢迎 5.3/5.4 的答案,特别是如果它们使它变得更容易,但我将无法使用它们)

4

6 回答 6

6

您明确写道您不想拥有全局变量,所以我也不建议您使用静态变量,因为它们实际上是全局变量 - 根本不需要这些变量。

在 PHP 5.2(及更早版本)中,如果您需要在回调中调用上下文,您可以通过使用它自己的类来创建您的上下文:

class CallContext
{
}

例如,您具有以下比较功能sort

class CallContext
{
    ...
    public function compare($a, $b)
    {
         return $this->weight($a) - $this->weight($b);
    }

    public function getCallback()
    {
         return array($this, 'compare');
    }
    ...
}

该函数可以用作以下回调,usort然后:

$context = new CallContext();

usort($array, $context->getCallback());

很直接。的私有实现CallContext::weight仍然缺失,从您的问题中我们知道它需要一些排序数据和信息。例如每条记录中国家 ID 的键名。让我们假设记录是 Stdclass 对象,因此要获得一条记录的权重,上下文类需要知道属性的名称、您自己定义的排序顺序以及那些未在自定义排序顺序(其他,其余)。

这些配置值由构造函数(简称ctor)给出,并存储为私有成员。然后缺少的weight函数根据该信息将记录转换为排序值:

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

用法现在扩展到以下内容:

$property = 'country';
$order = array(
    # country ID => sort key (lower is first)
    46 => 1,
    45 => 2
);
$context = new CallContext('country', $order);
usort($array, $context->getCallback());

使用相同的原理,您通常可以将任何带有use子句的 PHP 5.3 闭包转换为 PHP 5.2。子句中的变量use成为注入构造的私有成员。

这个变体不仅阻止了 static 的使用,它还使每个元素都有一些映射,并且由于两个元素被视为平等,它利用了某些weight函数的私有实现,该函数非常适用于usort.

我希望这是有帮助的。

于 2012-08-13T14:14:28.597 回答
2

你可能不想要一个全局变量,但你需要一个行为类似的东西。您可以使用具有静态方法和参数的类。它不会过多地污染全局范围,并且仍然可以按照您需要的方式运行。

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

然后像这样调用它:

CountryCompare::country_priorities = loadFromConfig();
CountryCompare::sort( $countries );
于 2012-08-12T14:51:53.760 回答
2

您可以使用闭包(PHP >= 5.3):

$weights = array( ... );
usort($records, function($a, $b) use ($weights) {
    // use $weights in here as usual and perform your sort logic
});
于 2012-08-14T07:24:12.423 回答
1

见演示: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);

输出

array
      'apr' => 
        array
          'FooN' => int 17
      'feb' => 
        array
          'Foo3' => int 15
      'mar' => 
        array
          'Foo1' => int 12
      'jan' => 
        array
          'Foo2' => int 8

另一个例子:-

使用 PHP 对多维数组进行排序

http://www.firsttube.com/read/sorting-a-multi-dimensional-array-with-php/

每隔一段时间,我就会发现自己有一个多维数组,我想按子数组中的值对其进行排序。我有一个可能看起来像这样的数组:

//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')
    );

问题是这样的:我想以“歌曲名(艺术家)”的格式回显我喜欢的歌曲,并且我想按艺术家的字母顺序来做。PHP 提供了许多用于对数组进行排序的函数,但没有一个可以在这里工作。ksort() 将允许我按键排序,但 $songs 数组中的键无关紧要。asort() 允许我对键进行排序和保留,但它会根据每个元素的值对 $songs 进行排序,这也是无用的,因为每个元素的值都是“array()”。usort() 是另一种可能的候选方法,可以进行多维排序,但它涉及构建回调函数,并且通常很冗长。甚至 PHP 文档中的示例也引用了特定的键。

所以我开发了一个快速函数来按子数组中键的值进行排序。请注意,此版本不区分大小写。请参阅下面的 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;
}

要在上面使用它,我只需键入:

$songs = subval_sort($songs,'artist'); 
print_r($songs);

这是您应该期望看到的:

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
        )

)

歌曲,按艺术家排序。

于 2012-08-08T18:12:58.240 回答
0

您的问题的答案确实在usort()函数中。但是,您需要做的是编写传递给它的函数,以便正确地为您加权。

大多数时候,你有类似的东西

if($a>$b)
{
    return $a;
}

但是你需要做的是一些类似的事情

if($a>$b || $someCountryID != 36)
{
    return $a;
}
else
{
    return $b;
}
于 2012-08-06T11:55:17.473 回答
0

您需要使用 ksort 按重量排序,而不是 usort。那会干净得多。

$weighted_data将您的数据排列在格式为 的关联数组中weight => country_data_struct。这是加权数据的一种非常直观的表示形式。然后运行

krsort($weighted_data)

于 2012-08-12T18:07:25.277 回答