按列值DESC对多维数组排序,然后按另一列DESC的长度排序


Sort multidimensional array on column values DESC, then by length of another column DESC

我需要对一些不是来自数据库的数据进行排序,但结构像sql结果集。

在MySQL中,我将编写如下查询,按两列对数据排序:

SELECT product, qty FROM stock ORDER BY qty DESC, LENGTH(product) DESC 

然而,在这种情况下,我需要用php执行这个排序逻辑。具体来说,首先按qty降序排序,然后按name降序的长度排序。

未分类的输入:

[
    ['name' => 'foo bar', 'qty' => 6],
    ['name' => 'foo bar bar foo', 'qty' => 10],
    ['name' => 'b', 'qty' => 5],
    ['name' => 'foo', 'qty' => 10],
    ['name' => 'bar', 'qty' => 6],
    ['name' => 'foo bar bar bar foo', 'qty' => 6],
]

排序后,我需要用name值作为键,qty值作为平面关联数组的值来重组数据完成后的数组看起来像这样:

所需输出:

[
    'foo bar bar foo' => 10,
    'foo' => 10,
    'foo bar bar bar foo' => 6,
    'foo bar' => 6,
    'bar' => 6,
    'b' => 5
]

看看这个问题的答案:PHP数组多重排序-按值然后按键?,看来array_multisort是要走的路。(我不是很确定array_multisort是如何工作的,我只是有点黑了这个,它似乎工作)。

试试这个:

$arr = array(
  'foo bar' => 6,
  'foo' => 10,
  'bar' => 6,
  'b' => 5,
  'foo bar bar bar foo' => 6,
  'foo bar bar foo' => 10
);
array_multisort(array_values($arr), SORT_DESC,
  array_map(create_function('$v', 'return strlen($v);'), array_keys($arr)),
  SORT_DESC, $arr);

演示:http://codepad.org/mAttNIV7

更新:添加了array_map,使其按字符串的长度排序,之前它只是做:

$str1 > $str2代替strlen($str1) > strlen($str2)

UPDATE 2:在PHP>= 5.3中,你可以用一个真正的匿名函数代替create_function

array_map(function($v){return strlen($v);}, array_keys($arr))

Demo 2: http://codepad.viper-7.com/6qrFwj

看一下php的usortuasort

你应该能够定义一个函数来对它进行排序

不确定它是否能很容易地与当前的数组一起工作但是它会

$array = array(
 array('name' => 'foo bar bar foo', 'qty' => 10 ),
 array('name' => 'foo', 'qty' => 6),
 array('name' => 'foo bar bar foo', 'qty' => 6 ),
 array('name' => 'foo bar', 'qty' => 6 )
);
uasort($array, 'arraySort');
function arraySort($a, $b)
{
    if($a['qty'] > $b['qty'])
        return 1;
    elseif($a['qty'] < $b['qty'])
        return -1;
    else
        if(strlen($a['name']) >= strlen($b['name']))
            return 1;
        else
            return -1;
}

基于长度对键进行排序的一个限制是:长度相等的键不会重新排序。假设我们需要按descending顺序按长度对键进行排序。

$arr = array(
    "foo 0" => "apple",
    "foo 1" => "ball",
    "foo 2 foo 0 foo 0" => "cat",
    "foo 2 foo 0 foo 1 foo 0" => "dog",
    "foo 2 foo 0 foo 1 foo 1" => "elephant",
    "foo 2 foo 1 foo 0" => "fish",
    "foo 2 foo 1 foo 1" => "giraffe"
);
debug($arr, "before sort");
$arrBad = $arr;
sortKeysDescBAD($arrBad);
debug($arrBad, "after BAD sort");
sortKeysDescGOOD($arr);
debug($arr, "after GOOD sort 2");
function sortKeysDescBAD(&$arrNew) {
    $arrKeysLength = array_map('strlen', array_keys($arrNew));
    array_multisort($arrKeysLength, SORT_DESC, $arrNew);
    //return max($arrKeysLength);
}
function sortKeysDescGOOD(&$arrNew) {
    uksort($arrNew, function($a, $b) {
        $lenA = strlen($a); $lenB = strlen($b);
        if($lenA == $lenB) {
            // If equal length, sort again by descending
            $arrOrig = array($a, $b);
            $arrSort = $arrOrig;
            rsort($arrSort);
            if($arrOrig[0] !== $arrSort[0]) return 1;
        } else {
            // If not equal length, simple
            return $lenB - $lenA;
        }
    });
}
function debug($arr, $title = "") {
    if($title !== "") echo "<br/><strong>{$title}</strong><br/>";
    echo "<pre>"; print_r($arr); echo "</pre><hr/>";
}

输出将是:

before sort
Array
(
    [foo 0] => apple
    [foo 1] => ball
    [foo 2 foo 0 foo 0] => cat
    [foo 2 foo 0 foo 1 foo 0] => dog
    [foo 2 foo 0 foo 1 foo 1] => elephant
    [foo 2 foo 1 foo 0] => fish
    [foo 2 foo 1 foo 1] => giraffe
)
after BAD sort
Array
(
    [foo 2 foo 0 foo 1 foo 0] => dog
    [foo 2 foo 0 foo 1 foo 1] => elephant
    [foo 2 foo 0 foo 0] => cat
    [foo 2 foo 1 foo 0] => fish
    [foo 2 foo 1 foo 1] => giraffe
    [foo 0] => apple
    [foo 1] => ball
)
after GOOD sort
Array
(
    [foo 2 foo 0 foo 1 foo 1] => elephant
    [foo 2 foo 0 foo 1 foo 0] => dog
    [foo 2 foo 1 foo 1] => giraffe
    [foo 2 foo 1 foo 0] => fish
    [foo 2 foo 0 foo 0] => cat
    [foo 1] => ball
    [foo 0] => apple
)

注意elephantdog(或其他)在两种排序方法中的顺序。第二种方法看起来更好。可能有更简单的方法来解决这个问题,但希望这能帮助到一些人…

从一个类似于sql结果集的输入数组开始,您可以清楚地使用包含3-way比较的usort(),然后是有条件的次3-way比较。完成后,可以将数据的qty列作为值隔离,并(假设所有name值都是唯一的)使用array_column()name列作为分配的键。比较时,将$b的数据写在操作符的左边,将$a的数据写在操作符的右边,实现降序排序。

代码(演示):

$array = [
    ['name' => 'foo bar bar foo', 'qty' => 6],
    ['name' => 'bah', 'qty' => 5],
    ['name' => 'foo foo bar foo', 'qty' => 10],
    ['name' => 'foo', 'qty' => 6],
    ['name' => 'foo bar', 'qty' => 6],
    ['name' => 'bar', 'qty' => 11],
];
usort($array, function($a, $b) {
    return $b['qty'] <=> $a['qty'] ?: strlen($b['name']) <=> strlen($a['name']);
});
var_export(array_column($array, 'qty', 'name'));
输出:

array (
  'bar' => 11,
  'foo foo bar foo' => 10,
  'foo bar bar foo' => 6,
  'foo bar' => 6,
  'foo' => 6,
  'bah' => 5,
)

使用comparison1 ?: comparison2的优点是,comparison2中的函数调用不会被执行,除非需要tie - break——这提高了效率。或者,使用array_multisort()将无条件地对所有qty值调用strlen()——即使它们不需要排序。

使用单个太空船操作符执行排序是完全有效的,但是下面的技术将在每次进行比较时调用两个函数。这将比我上面的代码段效率低,所以我不建议使用下面的代码段。

usort($array, function($a, $b) {
    return [$b['qty'], strlen($b['name']] <=> [$a['qty'], strlen($a['name'])];
});

注。当然,这也可以用array_multisort()来完成,我只是发现语法不那么简洁。

array_multisort(
    array_column($array, 'qty'),
    SORT_DESC,
    array_map(
        function($row) {
            return strlen($row['name']);
        },
        $array
    ),
    SORT_DESC,
    $array
);
var_export(array_column($array, 'qty', 'name'));

使用php中的排序函数。确保使用TRUE作为第二个参数来保留键。