使用array_multisort()自定义对多维数组进行排序

7

我有以下数组:

$array = [
    'note' => [],
    'year' => ['2011','2010', '2012'],
    'type' => ['conference', 'journal', 'conference'],
];

我使用以下函数根据类型字段和另一个数组来排序数组:

function array_multisort_by_order(array $array, $by, array $order)
{
    $order = array_flip($order);
    $params[] = $array[$by];
    foreach($params[0] as &$v) $v = $order[$v];
    foreach($array as &$v) $params[] = &$v; unset($v);
    call_user_func_array('array_multisort', $params);
    return $array;
}

当我调用以下函数时,出现了以下错误:
$array = array_multisort_by_order($array, 'type', array('conference', 'journal'));

print_r($array['type']);

错误:

Warning: array_multisort(): Array sizes are inconsistent.

我知道数组是不一致的。是否有更好的函数可用?
请检查:codepad 期望输出:
Array
(
[note] => Array
    (
        [0] => 
        [1] => 
        [2] => 
    )

[year] => Array
    (
        [0] => 2011
        [1] => 2012
        [2] => 2010
    )

[type] => Array
    (
        [0] => conference
        [1] => conference
        [2] => journal
    )

)

示例2:

数组

$array = [
    'note' => ['test1', 'test2'],
    'year' => ['2011', '2012'],
    'type' => ['conference', 'journal', 'conference'],
];

期望结果2

Array
(
[note] => Array
    (
        [0] => test1
        [1] => 
        [2] => tes2
    )

[year] => Array
    (
        [0] => 2011
        [1] => 2012
        [2] => 
    )

[type] => Array
    (
        [0] => conference
        [1] => conference
        [2] => journal
    )

)

3
嗯,如果有期望的输出结果,那么就不需要从代码中猜测了。你能提供吗? - Wrikken
好的,最后一个问题:子数组是否总是空的或长度一致,或者我们可以期望在这里有2个项目的子数组,如果是这样,我们如何处理呢?假设它们仍然与其他子数组的前两个项目相匹配? - Wrikken
@Wrikken:可以在这里找到一个工作示例:http://codepad.org/mjSBYEyi。不幸的是,子数组总是不一致的。我会提供另一个例子。添加了另一个例子。 - glarkou
好的,数字键很重要,明白了。 - Wrikken
2个回答

3

好的,那么,首先想到的解决方案之一是添加空值使它们保持一致:

function array_multisort_by_order(array $array, $by, array $order)
{
     $max = max(array_map('count',$array));
    //or, alternatively, depending on input (if there are no 'complete' subarrays):
    //$max = max(array_map(function($arr){return max(array_keys($arr));},$array))+1;

    //ADDITION: negative numeric keys:
    $min = min(array_map(function($arr){return min(array_keys($arr));},$array));
    $width = $max - min(0,$min);

    foreach($array as &$sub){
        // $addin = array_diff_key(array_fill(0,$max,null),$sub);
        // $addin changed for negative keys:
        $addin = array_diff_key(array_combine(range($min,$max),array_fill(0,$width,null)),$sub);
        $sub = $addin + $sub;
        ksort($sub);
    }
    $order = array_flip($order);
    $params[] = $array[$by];
    foreach($params[0] as &$v) $v = $order[$v];
    foreach($array as &$v) $params[] = &$v; unset($v);
    call_user_func_array('array_multisort', $params);
    //no closeures here:
    //foreach($array as &$sub) $sub = array_filter(function($a){return !is_null($a);},$sub);
    $filter = create_function('$a','return !is_null($a);');
    foreach($array as &$sub) $sub = array_filter($sub,$filter);
    return $array;
}

是的,老兄,我考虑过了。我用http://codepad.org/4QcAoemv,但它实际上没有将键添加到原始数组中。另外,在进行多重排序之后,有没有办法摆脱那些“空”值? - glarkou
糟糕,codepad 运行的版本低于 5.3,无法使用闭包...你是指这样的东西吗:http://codepad.org/5kMHlRc6 - Wrikken
可以,这样做可以完成任务。如果您能够检查一下代码,我尝试使用了一种更简单的方法来填充缺失的键值对:foreach ($array['type'] as $k => $v) { foreach($array as $element => $a) { $iterator = $array[$element]; if(!isset($iterator[$k])){ $iterator[$key] = ''; } } }。这暂时填充了 $iterator 数组而不是原始数组。再次感谢您的帮助,我将使用您的解决方案。我已将其标记为正确答案。请问您能否编辑一下您的回答? - glarkou
在最终函数中进行了编辑。 - Wrikken
谢谢,伙计。最后一个问题。我知道这很愚蠢,但不幸的是我有一个键值为-1,现在它是唯一导致问题的值。是否可能进行一些小修改来解决它? - glarkou
1
好的,希望你明白了,如果有错误,请随意编辑解决方案,我走了 ;) - Wrikken

0
  1. 填充行以保持一致的元素数量。
  2. 使用您的自定义排序标准生成查找数组,然后使用这些翻译值填充第一个排序参数。
  3. 将所有行作为引用变量推入排序参数的有效负载中。
  4. 使用array_multisort()进行排序。不需要return,因为所有排序操作都是通过引用完成的。

代码:(演示

function array_multisort_custom(array &$array, $columnKey, array $order)
{
    // guard clause/condition
    if (!array_key_exists($columnKey, $array)) {
        throw new Exception('Nominated sorting column not found');
    }
    
    // pad rows to consistent size
    $maxCount = max(array_map('count', $array));
    array_walk($array, fn(&$row) => $row = array_pad($row, $maxCount, null));

    // populate first sorting parameter with custom order array
    $priority = array_flip($order);
    $default = count($order);
    foreach ($array[$columnKey] as $v) {
        $params[0][] = $priority[$v] ?? $default;
    }
    
    // assign reference variables to parameter array for all rows
    foreach ($array as &$row) {
        $params[] = &$row;
    }
    
    array_multisort(...$params);
}

array_multisort_custom($array, 'type', ['conference', 'journal']);
var_export($array);

如果输入数组的第一层键是数字,上面的代码片段可以更加紧凑,最后一个foreach()也可以被移除(因为数字键可以在array_multisort()内使用展开运算符进行解包)。演示或者甚至演示 相关答案:使用动态数量的参数/规则/数据使用array_multisort()对数组进行排序

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接