按多列对多维数组进行排序

Posted

技术标签:

【中文标题】按多列对多维数组进行排序【英文标题】:Sort multidimensional array by multiple columns 【发布时间】:2011-03-15 01:28:49 【问题描述】:

我正在尝试通过多个键对多维数组进行排序,但我不知道从哪里开始。我查看了uasort(),但不太确定如何编写我需要的函数。

我需要按stateevent_typedate_start 排序。

我的数组如下所示:

[
    ['ID' => 1, 'title' => 'Boring Meeting',  'date_start' => '2010-07-30', 'event_type' => 'meeting', 'state' => 'new-york'],
    ['ID' => 2, 'title' => 'Find My Stapler', 'date_start' => '2010-07-22', 'event_type' => 'meeting', 'state' => 'new-york'],
    ['ID' => 3, 'title' => 'Mario Party',     'date_start' => '2010-07-22', 'event_type' => 'party',   'state' => 'new-york'],
    ['ID' => 4, 'title' => 'Duct Tape Party', 'date_start' => '2010-07-28', 'event_type' => 'party',   'state' => 'california']
]

我想要的结果是:

[
    ['ID' => 4, 'title' => 'Duct Tape Party', 'date_start' => '2010-07-28', 'event_type' => 'party',   'state' => 'california']
    ['ID' => 2, 'title' => 'Find My Stapler', 'date_start' => '2010-07-22', 'event_type' => 'meeting', 'state' => 'new-york'],
    ['ID' => 1, 'title' => 'Boring Meeting',  'date_start' => '2010-07-30', 'event_type' => 'meeting', 'state' => 'new-york'],
    ['ID' => 3, 'title' => 'Mario Party',     'date_start' => '2010-07-22', 'event_type' => 'party',   'state' => 'new-york'],
]

【问题讨论】:

……你想怎么排序? Sorting multidimensional array in php 的可能重复项 【参考方案1】:

你需要array_multisort

$mylist = array(
    array('ID' => 1, 'title' => 'Boring Meeting', 'event_type' => 'meeting'),
    array('ID' => 2, 'title' => 'Find My Stapler', 'event_type' => 'meeting'),
    array('ID' => 3, 'title' => 'Mario Party', 'event_type' => 'party'),
    array('ID' => 4, 'title' => 'Duct Tape Party', 'event_type' => 'party')
);

# get a list of sort columns and their data to pass to array_multisort
$sort = array();
foreach($mylist as $k=>$v) 
    $sort['title'][$k] = $v['title'];
    $sort['event_type'][$k] = $v['event_type'];

# sort by event_type desc and then title asc
array_multisort($sort['event_type'], SORT_DESC, $sort['title'], SORT_ASC,$mylist);

从 PHP 5.5.0 开始:

array_multisort(array_column($mylist, 'event_type'), SORT_DESC,
                array_column($mylist, 'title'),      SORT_ASC,
                $mylist);

$mylist 现在是:

array (
  0 => 
  array (
    'ID' => 4,
    'title' => 'Duct Tape Party',
    'event_type' => 'party',
  ),
  1 => 
  array (
    'ID' => 3,
    'title' => 'Mario Party',
    'event_type' => 'party',
  ),
  2 => 
  array (
    'ID' => 1,
    'title' => 'Boring Meeting',
    'event_type' => 'meeting',
  ),
  3 => 
  array (
    'ID' => 2,
    'title' => 'Find My Stapler',
    'event_type' => 'meeting',
  ),
)

【讨论】:

@Rob 我很好奇你会如何排序 date_start 什么是“胶带派对”? 对于 PHP array_column 函数 github.com/ramsey/array_column 的 polyfill。因此,可以在旧版本上使用第二个代码 sn-p 的更优雅的方式。 @gdaniel 请跟我到我的地下室一探究竟。【参考方案2】:
class Sort 
    private $actual_order = 'asc';
    private $actual_field = null;

    public function compare_arrays($array1, $array2) 

        if ($array1[$this->actual_field] == $array2[$this->actual_field]) 
            return 0;
        
        elseif ($array1[$this->actual_field] > $array2[$this->actual_field]) 
            return ($this->actual_order == 'asc' ? 1 : -1);
        
        else 
            return ($this->actual_order == 'asc' ? -1 : 1);
        

    


    public function order_array(&$array) 

        usort($array, array($this, 'compare_arrays'));

    


    public function __construct ($field, $actual_order = 'asc') 
        $this->actual_field = $field;
        $this->actual_order = $actual_order;
    



// use

$sort = new Sort ("state");

$sort->order_array($array);

【讨论】:

这个答案缺少教育解释。这个答案如何解决OP关于在三列上对多维数组进行排序的问题?【参考方案3】:

您可以使用usort 完成此操作。 $cmp_function 参数可以是:

function my_sorter($a, $b) 
    $c = strcmp($a['state'], $b['state']);
    if($c != 0) 
        return $c;
    

    $c = strcmp($a['event_type'], $b['event_type']);
    if($c != 0) 
        return $c;
    

    return strcmp($a['date_start'], $b['date_start']);

对于 PHP 5.3 中的任意数量的字段,您可以使用闭包来创建比较函数:

function make_cmp($fields, $fieldcmp='strcmp') 
    return function ($a, $b) use (&$fields) 
        foreach ($fields as $field) 
            $diff = $fieldcmp($a[$field], $b[$field]);
            if($diff != 0) 
                return $diff;
            
        
        return 0;
    


usort($arr, make_cmp(array('state', 'event_type', 'date_start')))

对于 PHP 5.3 中任意数量的不同类型的字段:

function make_cmp($fields, $dfltcmp='strcmp') 
    # assign array in case $fields has no elements
    $fieldcmps = array();
    # assign a comparison function to fields that aren't given one
    foreach ($fields as $field => $cmp) 
        if (is_int($field) && ! is_callable($cmp)) 
            $field = $cmp;
            $cmp = $dfltcmp;
        
        $fieldcmps[$field] = $cmp;
    
    return function ($a, $b) use (&$fieldcmps) 
        foreach ($fieldcmps as $field => $cmp) 
            $diff = call_user_func($cmp, $a[$field], $b[$field]);
            if($diff != 0) 
                return $diff;
            
        
        return 0;
    


function numcmp($a, $b) 
    return $a - $b;

function datecmp($a, $b) 
    return strtotime($a) - strtotime($b);

/**
 * Higher priority come first; a priority of 2 comes before 1.
 */
function make_evt_prio_cmp($priorities, $default_priority) 
    return function($a, $b) use (&$priorities) 
        if (isset($priorities[$a])) 
            $prio_a = $priorities[$a];
         else 
            $prio_a = $default_priority;
        
        if (isset($priorities[$b])) 
            $prio_b = $priorities[$b];
         else 
            $prio_b = $default_priority;
        
        return $prio_b - $prio_a;
    ;


$event_priority_cmp = make_evt_prio_cmp(
    array('meeting' => 5, 'party' => 10, 'concert' => 7), 
    0);

usort($arr, make_cmp(array('state', 'event' => $event_priority_cmp, 'date_start' => 'datecmp', 'id' => 'numcmp')))

【讨论】:

你可以稍微简化嵌套,我认为你需要对日期做更多的事情,但目前看来这种方法是最好的。 示例数组中使用的 '%Y-%m-%d' 格式的好处是字符串比较适用于日期比较。【参考方案4】:

我尝试了下面的代码,我成功了

数组代码

$songs =  array(
        '1' => array('artist'=>'Smashing Pumpkins', 'songname'=>'Soma'),
        '2' => array('artist'=>'The Decemberists', 'songname'=>'The Island'),
        '3' => array('artist'=>'Fleetwood Mac', 'songname' =>'Second-hand News')
);

调用数组排序函数

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

数组排序函数

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;

if数组逆向排序函数

function subval_sort($a,$subkey) 
        foreach($a as $k=>$v) 
            $b[$k] = strtolower($v[$subkey]);
        
        arsort($b);
        foreach($b as $key=>$val) 
            $c[] = $a[$key];
        
        return $c;
    

【讨论】:

这仅按列排序(以一种不优雅的方式),但 OP 的问题要求排序标准涉及三个列值。充其量,这是对另一个问题的正确答案。【参考方案5】:

改进@Stijn Leenknegt 的天才代码,这是我的 2 cent 实用函数:

$data[] = array('volume' => 67, 'edition' => 2);
$data[] = array('volume' => 86, 'edition' => 1);
$data[] = array('volume' => 85, 'edition' => 6);
$data[] = array('volume' => 98, 'edition' => 2);
$data[] = array('volume' => 86, 'edition' => 6);
$data[] = array('volume' => 67, 'edition' => 7);

function make_cmp(array $sortValues)

    return function ($a, $b) use (&$sortValues) 
        foreach ($sortValues as $column => $sortDir) 
            $diff = strcmp($a[$column], $b[$column]);
            if ($diff !== 0) 
                if ('asc' === $sortDir) 
                    return $diff;
                
                return $diff * -1;
            
        
        return 0;
    ;


usort($data, make_cmp(['volume' => "desc", 'edition' => "asc"]));

【讨论】:

if ($diff) return $diff * ($sortDir === 'asc' ? 1 : -1); 【参考方案6】:

如果要对多维数组进行排序

第一个数组是:

$results['total_quote_sales_person_wise']['quote_po'];

第二个是:

$results['total_quote_sales_person_wise']['quote_count'];

这两个多维数组你想一次降序排序然后使用这个代码:

array_multisort($results['total_quote_sales_person_wise']['quote_po'],SORT_DESC, $results['total_quote_sales_person_wise']['quote_count'],SORT_DESC);

【讨论】:

【参考方案7】:

PHP7 使用宇宙飞船运算符 (<=>) 又名“组合比较运算符”或“三路比较运算符”,让多列排序变得超级简单。

资源:https://wiki.php.net/rfc/combined-comparison-operator

按多列排序就像在运算符两侧编写平衡/关系数组一样简单。轻松搞定!

$a 值在spaceship 运算符的左侧,$b 值在右侧时,使用 ASCending 排序。

$b 值在 spaceship 运算符的左侧,$a 值在右侧时,使用 DESCending 排序。

当 spaceship 运算符比较两个数字字符串时,它会将它们作为数字进行比较——因此您可以自动获得自然排序。

我没有使用uasort(),因为我认为没有必要保留原始索引。

代码:(Demo) -- 按state ASC 排序,然后是event_type ASC,然后是date_start ASC

$array = [
    ['ID' => 1, 'title' => 'Boring Meeting', 'date_start' => '2010-07-30', 'event_type' => 'meeting', 'state' => 'new-york'],
    ['ID' => 2, 'title' => 'Find My Stapler', 'date_start' => '2010-07-22', 'event_type' => 'meeting', 'state' => 'new-york'],
    ['ID' => 3, 'title' => 'Mario Party', 'date_start' => '2010-07-22', 'event_type' => 'party', 'state' => 'new-york'],
    ['ID' => 4, 'title' => 'Duct Tape Party', 'date_start' => '2010-07-28', 'event_type' => 'party', 'state' => 'california']
];

usort($array, function($a, $b) 
    return [$a['state'], $a['event_type'], $a['date_start']]
           <=>
           [$b['state'], $b['event_type'], $b['date_start']];
);

var_export($array);

输出

array (
  0 => 
  array (
    'ID' => 4,
    'title' => 'Duct Tape Party',
    'date_start' => '2010-07-28',
    'event_type' => 'party',
    'state' => 'california',
  ),
  1 => 
  array (
    'ID' => 2,
    'title' => 'Find My Stapler',
    'date_start' => '2010-07-22',
    'event_type' => 'meeting',
    'state' => 'new-york',
  ),
  2 => 
  array (
    'ID' => 1,
    'title' => 'Boring Meeting',
    'date_start' => '2010-07-30',
    'event_type' => 'meeting',
    'state' => 'new-york',
  ),
  3 => 
  array (
    'ID' => 3,
    'title' => 'Mario Party',
    'date_start' => '2010-07-22',
    'event_type' => 'party',
    'state' => 'new-york',
  ),
)

附言PHP7.4 及更高版本的箭头语法 (Demo)...

usort($array, fn($a, $b) =>
    [$a['state'], $a['event_type'], $a['date_start']]
    <=>
    [$b['state'], $b['event_type'], $b['date_start']]
);

array_multisort() 和对每个排序标准调用 array_column() 的等效技术是:(Demo)

array_multisort(
    array_column($array, 'state'),
    array_column($array, 'event_type'),
    array_column($array, 'date_start'),
    $array
);

【讨论】:

既然我们都在使用 PHP 7,这应该是正确的答案。【参考方案8】:

也许它可以帮助某人:

// data to sort
$output = array(
        array('ID' => 1, 'title' => 'Boring Meeting', 'event_type' => 'meeting'),
        array('ID' => 2, 'title' => 'Find My Stapler', 'event_type' => 'meeting'),
        array('ID' => 3, 'title' => 'Mario Party', 'event_type' => 'party'),
        array('ID' => 4, 'title' => 'Duct Tape Party', 'event_type' => 'party')
);

// multi column, multi direction order by
$body['order_by'] = array(
        array("field"=> "event_type", "order"=> "desc"),
        array("field"=> "title", "order"=> "asc"),
        array("field"=> "ID", "order"=> "asc"),
);

$output = $this->multiColumnMultiDirectionSort($body, $output);


public function multiColumnMultiDirectionSort(array $body, array $output)

    // get order fields and its direction in proper format
    $orderFieldDirection = [];
    if (!empty($body['order_by']) && is_array($body['order_by'])) 
        foreach ($body['order_by'] as $order) 
            $orderDirection = $order['order'] == "desc" ? SORT_DESC : SORT_ASC; // we need format that array_multisort supports
            $orderFieldDirection[$order['field']] = $orderDirection;
        
    

    if (!empty($orderFieldDirection)) 
        // get the list of sort columns and their data in the format that is required by array_multisort
        $amParams = [];
        $sort = [];
        foreach ($orderFieldDirection as $field => $order) 
            foreach ($output as $k => $v) 
                $sort[$field][$k] = $v[$field];
            

            $amParams[] = $sort[$field];
            $amParams[] = $order;
            $amParams[] = SORT_REGULAR; // this is not needed, but we can keep as it might come handy in the future
        

        $amParams[] = &$output; // very important to pass as reference
        call_user_func_array("array_multisort", $amParams);
    

    return $output;

【讨论】:

以上是关于按多列对多维数组进行排序的主要内容,如果未能解决你的问题,请参考以下文章

按子项的值对多维数组进行排序

如何按列对多维数组进行排序?

如何按值对多维数组进行排序

php 按特定键对多维数组进行排序

PHP 按属性对多维数组进行排序和查询

PHP按包含日期的元素对多维数组进行排序