按自定义顺序对php数组排序

Hon*_*ner 52 php arrays sorting multidimensional-array

我有一个数组数组:

Array ( 
    [0] => Array (
        [id] = 7867867,
        [title] = 'Some Title'),
    [1] => Array (
        [id] = 3452342,
        [title] = 'Some Title'),
    [2] => Array (
        [id] = 1231233,
        [title] = 'Some Title'),
    [3] => Array (
        [id] = 5867867,
        [title] = 'Some Title')
)
Run Code Online (Sandbox Code Playgroud)

需要按特定顺序进行:

  1. 3452342
  2. 5867867
  3. 7867867
  4. 1231233

我该怎么做呢?我之前已对数组进行了排序,并阅读了大量有关它的其他帖子,但它们始终是基于比较的(即valueA <valueB).

感谢帮助.

sal*_*the 109

您可以使用它usort()来精确地指示数组的排序方式.在这种情况下,$order阵列可以在比较功能中使用.

以下示例使用a closure来简化生活.

$order = array(3452342, 5867867, 7867867, 1231233);
$array = array(
    array('id' => 7867867, 'title' => 'Some Title'),
    array('id' => 3452342, 'title' => 'Some Title'),
    array('id' => 1231233, 'title' => 'Some Title'),
    array('id' => 5867867, 'title' => 'Some Title'),
);

usort($array, function ($a, $b) use ($order) {
    $pos_a = array_search($a['id'], $order);
    $pos_b = array_search($b['id'], $order);
    return $pos_a - $pos_b;
});

var_dump($array);
Run Code Online (Sandbox Code Playgroud)

这项工作的关键是将要比较的值作为数组中ids 的位置$order.

比较函数通过在$order阵列中找到要比较的两个项的id的位置来工作.如果在数组$a['id']之前,那么函数的返回值将是负数(不那么"浮动"到顶部).如果在此之后,该函数返回一个正数(更大,因此"下沉").$b['id']$order$a$a['id']$b['id']$a

最后,使用封闭没有特殊原因; 这只是我快速编写这些一次性功能的首选方式.它同样可以使用普通的命名函数.

  • 好的,经过 5 秒的研究,我发现了这个:http://stackoverflow.com/questions/348410/sort-an-array-by-keys-based-on-another-array。 (2认同)

Sal*_*n A 16

为此附加要求扩展salathe的答案:

现在当我向数组中添加项而不是排序时会发生什么?我不关心它们出现的顺序,只要它出现在我指定的顺序之后.

您需要在排序功能中添加两个附加条件:

  1. "不关心"项目必须被视为大于"关心"项目
  2. 必须认为两个"不关心"的物品是平等的

所以修改后的代码是:

$order = array(
    3452342,
    5867867,
    7867867,
    1231233
);
$array = array(
    array("id" => 7867867, "title" => "Must Be #3"),
    array("id" => 3452342, "title" => "Must Be #1"),
    array("id" => 1231233, "title" => "Must Be #4"),
    array("id" => 5867867, "title" => "Must Be #2"),
    array("id" => 1111111, "title" => "Dont Care #1"),
    array("id" => 2222222, "title" => "Dont Care #2"),
    array("id" => 3333333, "title" => "Dont Care #3"),
    array("id" => 4444444, "title" => "Dont Care #4")
);
function custom_compare($a, $b){
    global $order;
    $a = array_search($a["id"], $order);
    $b = array_search($b["id"], $order);
    if($a === false && $b === false) { // both items are dont cares
        return 0;                      // a == b
    }
    else if ($a === false) {           // $a is a dont care item
        return 1;                      // $a > $b
    }
    else if ($b === false) {           // $b is a dont care item
        return -1;                     // $a < $b
    }
    else {
        return $a - $b;
    }
}
shuffle($array);  // for testing
var_dump($array); // before
usort($array, "custom_compare");
var_dump($array); // after
Run Code Online (Sandbox Code Playgroud)

输出:

Before                         |  After
-------------------------------+-------------------------------
array(8) {                     |  array(8) {
  [0]=>                        |    [0]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(4444444)               |      int(3452342)
    ["title"]=>                |      ["title"]=>
    string(12) "Dont Care #4"  |      string(10) "Must Be #1"
  }                            |    }
  [1]=>                        |    [1]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(3333333)               |      int(5867867)
    ["title"]=>                |      ["title"]=>
    string(12) "Dont Care #3"  |      string(10) "Must Be #2"
  }                            |    }
  [2]=>                        |    [2]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(1231233)               |      int(7867867)
    ["title"]=>                |      ["title"]=>
    string(10) "Must Be #4"    |      string(10) "Must Be #3"
  }                            |    }
  [3]=>                        |    [3]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(1111111)               |      int(1231233)
    ["title"]=>                |      ["title"]=>
    string(12) "Dont Care #1"  |      string(10) "Must Be #4"
  }                            |    }
  [4]=>                        |    [4]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(5867867)               |      int(2222222)
    ["title"]=>                |      ["title"]=>
    string(10) "Must Be #2"    |      string(12) "Dont Care #2"
  }                            |    }
  [5]=>                        |    [5]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(2222222)               |      int(1111111)
    ["title"]=>                |      ["title"]=>
    string(12) "Dont Care #2"  |      string(12) "Dont Care #1"
  }                            |    }
  [6]=>                        |    [6]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(3452342)               |      int(3333333)
    ["title"]=>                |      ["title"]=>
    string(10) "Must Be #1"    |      string(12) "Dont Care #3"
  }                            |    }
  [7]=>                        |    [7]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(7867867)               |      int(4444444)
    ["title"]=>                |      ["title"]=>
    string(10) "Must Be #3"    |      string(12) "Dont Care #4"
  }                            |    }
}                              |  }
Run Code Online (Sandbox Code Playgroud)


mic*_*usa 5

使用带有迭代调用方法的其他答案array_search()效率不高。通过重组/翻转“顺序”查找数组,您可以完全省略所有array_search()调用-使您的任务更加高效和简短。我将使用最现代的“太空飞船操作员”(<=>),但较早的技术在比较线中的作用相同。

方法1 - usort当所有的id值存在$order演示

$order=array_flip([3452342,5867867,7867867,1231233]);  // restructure with values as keys, and keys as order (ASC)
// generating $order=[3452342=>0,5867867=>1,7867867=>2,1231233=>3];
$array=[
    ['id'=>7867867,'title'=>'Some Title'],
    ['id'=>3452342,'title'=>'Some Title'],
    ['id'=>1231233,'title'=>'Some Title'],
    ['id'=>5867867,'title'=>'Some Title']
];

usort($array,function($a,$b)use($order){
    return $order[$a['id']]<=>$order[$b['id']];
    // when comparing ids 3452342 & 1231233, the actual comparison is 0 vs 3
});
// uasort() if you want to preserve keys

var_export($array);
Run Code Online (Sandbox Code Playgroud)

方法2 - usort当一些id中不存在的值$order演示
*注意,isset()是不是更便宜的通话array_search()

$order=array_flip([3452342,5867867,7867867,1231233]);  // restructure with values as keys, and keys as order (ASC)
// generating $order=[3452342=>0,5867867=>1,7867867=>2,1231233=>3];
$outlier=1+max($order);
// generating $outlier=4
$array=[
    ['id'=>7867867,'title'=>'Some Title'],
    ['id'=>3452342,'title'=>'Some Title'],
    ['id'=>'foo','title'=>'Some Title'],
    ['id'=>1231233,'title'=>'Some Title'],
    ['id'=>'bar','title'=>'Some Title'],
    ['id'=>5867867,'title'=>'Some Title']
];

usort($array,function($a,$b)use(&$order,$outlier){  // make $order modifiable with &
    if(!isset($order[$a['id']])){$order[$a['id']]=$outlier;}  // update lookup array with [id]=>[outlier number]
    if(!isset($order[$b['id']])){$order[$b['id']]=$outlier;}  // and again
    return $order[$a['id']]<=>$order[$b['id']];
});

var_export($array);
Run Code Online (Sandbox Code Playgroud)

替代方法#2 - usort当一些id不存在的值$order

...我还想提一提,在某些情况下,避免在调用之前isset()充分准备$order数组比避免重复调用double 可能没有吸引力usort()

这种单行代码将确保没有遗漏任何id值,从而消除了排序功能内部不需要比较行的任何其他问题。(完整的片段演示

$order=array_replace(array_fill_keys(array_column($array,'id'),$outlier),$order);
Run Code Online (Sandbox Code Playgroud)


Jos*_*ann 5

更高效的解决方案

$dict = array_flip($order);
$positions = array_map(function ($elem) use ($dict) { return $dict[$elem['id']] ?? INF; }, $array);
array_multisort($positions, $array);
Run Code Online (Sandbox Code Playgroud)

不要在每次比较时重新计算位置

当你的数组很大或者获取 id 的成本更高时,使用usort()可能会很糟糕,因为你会为每次比较重新计算 id。尝试array_multisort()使用预先计算的位置(参见下面的示例中mediumsort的 或fastsort),这并不复杂。

此外,在每次比较时在 order 数组中搜索 id(如在接受的答案中)并不会提高性能,因为每次比较都会对其进行迭代。计算一次。

在下面的代码片段中,您可以看到主要的三个排序函数:

  • slowsort
    已接受的答案。在每次比较时搜索位置。
  • mediumsort
    slowsort通过提前计算位置 进行改进
  • fastsort
    mediumsort通过避免完全搜索 而得到改进。

请注意,这些句柄元素的 id 不是通过提供后备值按顺序给出的INF。如果您的订单数组与原始数组的 id 1 对 1 匹配,那么请避免一起排序,只需将元素插入到正确的位置即可。我添加了一个函数cheatsort来完成这个任务。

更一般地,您可以按权重对数组进行排序(请参见weightedsort示例)。确保只计算一次重量,以获得良好的性能。

性能(对于长度为 1000 的数组)

fastsort     about  1 ms
mediumsort   about  3 ms
slowsort     about 60 ms
Run Code Online (Sandbox Code Playgroud)

提示:对于较大的数组,差异会变得更糟。

排序功能对比

fastsort     about  1 ms
mediumsort   about  3 ms
slowsort     about 60 ms
Run Code Online (Sandbox Code Playgroud)