【问题标题】:PHP sort array by priorityPHP按优先级排序数组
【发布时间】:2018-11-03 18:43:20
【问题描述】:

我需要一种按优先级对项目数组进行排序的方法。

这是我目前所做的工作:

function arraySortPriority(array &$array, $offset, array $priorities)
{
    uasort($array, function ($a, $b) use ($offset, $priorities) {
        if (!isset($a[$offset])) {
            $a[$offset] = null;
        }
        if (!isset($b[$offset])) {
            $b[$offset] = null;
        }
        if ($a[$offset] == $b[$offset]) {
            return 0;
        }
        $aPriority = isset($priorities[$a[$offset]])
            ? $priorities[$a[$offset]]
            : null;
        $bPriority = isset($priorities[$b[$offset]])
            ? $priorities[$b[$offset]]
            : null;
        return $aPriority > $bPriority ? -1 : 1;
    });
}

// an array to sort
$array = [
    ['type' => 'A'],
    ['type' => 'A'],
    ['type' => 'B'],
    ['type' => 'B'],
    ['type' => 'C'],
    ['type' => 'C'],
    ['type' => 'D'],
    ['type' => 'D'],
    ['type' => 'E'],
    ['type' => 'E'],
    ['type' => 'F'],
    ['type' => 'F'],
    ['type' => 'G'],
    ['type' => 'G'],
    ['type' => 'H'],
    ['type' => 'H'],
    ['type' => 'Foo'],
    ['type' => 'Foo'],
    ['type' => 'Bar'],
    ['type' => 'Bar'],
    [0 => 'no type should be last'],
    [0 => 'no type should be last'],
];
// shuffle the array
shuffle($array);
// set priorities
$priorities = [
    'A' => 8,
    'B' => 7,
    'C' => 6,
    'D' => 5,
    'E' => 4,
    'F' => 3,
    'G' => 2,
    'H' => 1,
];
// call
arraySortPriority($array, 'type', $priorities);
// test output
foreach ($array as $item) {
    if (isset($item['type'])) {
        echo "{$item['type']}\r\n";
    } else {
        $values = array_values($item);
        echo reset($values) . PHP_EOL;
    }
}

预期:

A
A
B
B
C
C
D
D
E
E
F
F
G
G
H
H
Foo
Foo
Bar
Bar
no type should be last
no type should be last

实际:

A
A
B
B
C
C
D
D
E
E
F
F
G
G
H
H
no type should be last   <-- should be at bottom
no type should be last   <-- should be at bottom
Bar
Bar
Foo
Foo

问题是没有被赋予$offset 的项目应该总是排在底部。

这意味着no type should be last 的排序应始终低于FooBar

我该怎么做?

【问题讨论】:

    标签: php arrays sorting offset


    【解决方案1】:

    您没有处理两个优先级相同的情况。

    解决方案 1

    只需将以下内容添加到三元之前的函数中:

    if ($aPriority == $bPriority)
        return 0;
    

    所以它应该是这样的:

    uasort($array, function ($a, $b) use ($offset, $priorities) {
        if (!isset($a[$offset])) {
            $a[$offset] = null;
        }
        if (!isset($b[$offset])) {
            $b[$offset] = null;
        }
        if ($a[$offset] == $b[$offset]) {
            return 0;
        }
    
        $aPriority = isset($priorities[$a[$offset]])
            ? $priorities[$a[$offset]]
            : null;
        $bPriority = isset($priorities[$b[$offset]])
            ? $priorities[$b[$offset]]
            : null;
    
        if ($aPriority == $bPriority)
            return 0;
    
        return $aPriority > $bPriority ? -1 : 1;
    });
    

    否则,您只需假设 $aPriority 小于 $bPriority(如果它不大于但可以相等)。

    解决方案 2

    另一种无需额外 if case 的方法是将优先级设置为 0 而不是 null(如果未设置):

    $aPriority = isset($priorities[$a[$offset]])
        ? $priorities[$a[$offset]]
        : 0;
    $bPriority = isset($priorities[$b[$offset]])
        ? $priorities[$b[$offset]]
        : 0;
    

    然后返回减法:

    return $bPriority - $aPriority;
    

    所以它看起来像这样:

    uasort($array, function ($a, $b) use ($offset, $priorities) {
        if (!isset($a[$offset])) {
            $a[$offset] = null;
        }
        if (!isset($b[$offset])) {
            $b[$offset] = null;
        }
        if ($a[$offset] == $b[$offset]) {
            return 0;
        }
    
        $aPriority = isset($priorities[$a[$offset]])
            ? $priorities[$a[$offset]]
            : 0;
        $bPriority = isset($priorities[$b[$offset]])
            ? $priorities[$b[$offset]]
            : 0;
    
        return $bPriority - $aPriority;
    });
    

    【讨论】:

      【解决方案2】:
      <?php 
      
      // an array to sort
      $array = [
          ['type' => 'A'],
          ['type' => 'A'],
          ['type' => 'B'],
          ['type' => 'B'],
          ['type' => 'C'],
          ['type' => 'C'],
          ['type' => 'D'],
          ['type' => 'D'],
          ['type' => 'E'],
          ['type' => 'E'],
          ['type' => 'F'],
          ['type' => 'F'],
          ['type' => 'G'],
          ['type' => 'G'],
          ['type' => 'H'],
          ['type' => 'H'],
          ['type' => 'Foo'],
          ['type' => 'Foo'],
          ['type' => 'Bar'],
          ['type' => 'Bar'],
          [0 => 'no type should be last'],
          [0 => 'no type should be last'],
      ];
      // shuffle the array
      shuffle($array);
      // set priorities
      $priorities = [
          'A' => 8,
          'B' => 7,
          'C' => 6,
          'D' => 5,
          'E' => 4,
          'F' => 3,
          'G' => 2,
          'H' => 1,
      ];
      
      uasort($array,function($a,$b) use ($priorities){
              if(!isset($a['type'])){
                  if(!isset($b['type'])) return -1;
                  return 1;
              }else if(!isset($b['type'])){
                  return -1;
              }
      
              if(isset($priorities[$a['type']])){
                  if(!isset($priorities[$b['type']])) return -1;
      
                  if($priorities[$a['type']] > $priorities[$b['type']]) return -1;
                  else if($priorities[$a['type']] < $priorities[$b['type']]) return 1;
      
              }else if(isset($priorities[$b['type']])){
                  return 1;           
              }
      
              return 0;
      });
      
      echo "<pre>";
      print_r($array);
      

      输出

      Array
      (
          [21] => Array
              (
                  [type] => A
              )
      
          [8] => Array
              (
                  [type] => A
              )
      
          [18] => Array
              (
                  [type] => B
              )
      
          [20] => Array
              (
                  [type] => B
              )
      
          [6] => Array
              (
                  [type] => C
              )
      
          [16] => Array
              (
                  [type] => C
              )
      
          [11] => Array
              (
                  [type] => D
              )
      
          [1] => Array
              (
                  [type] => D
              )
      
          [7] => Array
              (
                  [type] => E
              )
      
          [17] => Array
              (
                  [type] => E
              )
      
          [13] => Array
              (
                  [type] => F
              )
      
          [5] => Array
              (
                  [type] => F
              )
      
          [15] => Array
              (
                  [type] => G
              )
      
          [9] => Array
              (
                  [type] => G
              )
      
          [4] => Array
              (
                  [type] => H
              )
      
          [0] => Array
              (
                  [type] => H
              )
      
          [19] => Array
              (
                  [type] => Bar
              )
      
          [10] => Array
              (
                  [type] => Foo
              )
      
          [14] => Array
              (
                  [type] => Bar
              )
      
          [12] => Array
              (
                  [type] => Foo
              )
      
          [2] => Array
              (
                  [0] => no type should be last
              )
      
          [3] => Array
              (
                  [0] => no type should be last
              )
      
      )
      

      【讨论】:

      【解决方案3】:

      我决定使用@vivek_23 (edited|fixed) 解决方案。

      我删除了不必要的 else 并将返回值更改为零,如果 f.e.两个偏移量都没有设置。

      如果 a 和 b 缺少偏移量或没有优先级,则函数应该返回 0 imo。

      工作代码:

      uasort($array, function ($a, $b) use ($offset, $priorities) {
          if (!isset($a[$offset])) {
              return !isset($b[$offset])
                  ? 0
                  : 1; // down
          } elseif (!isset($b[$offset])) {
              return -1; // up
          }
          if (isset($priorities[$a[$offset]])) {
              if (!isset($priorities[$b[$offset]])) {
                  return -1; // up
              }
              return $priorities[$a[$offset]] > $priorities[$b[$offset]]
                  ? -1 // up
                  : 1; // down
          }
          return isset($priorities[$b[$offset]])
              ? 1 // down
              : 0;
      });
      

      这允许我使用负优先级、浮动优先级并且我不会覆盖优先级(参见 -99999999 @Eddie 解决方案)。

      测试

      // expect A, A, B, B, C, C, ... "no type ..." at bottom
      $priorities = [
          'A' => 8,
          'B' => 7,
          'C' => 6,
          'D' => 5,
          'E' => 4,
          'F' => 3,
          'G' => 2,
          'H' => 1,
      ];
      
      // expect "no type ..." at bottom, ..., C, C, B, B, A, A
      $priorities = [
          'A' => -8,
          'B' => -7,
          'C' => -6,
          'D' => -5,
          'E' => -4,
          'F' => -3,
          'G' => -2,
          'H' => -1,
      ];
      
      // expect B, B, A, A, C, C, ... "no type ..." at bottom
      $priorities = [
          'A' => 6.5,
          'B' => 7,
          'C' => 6,
          'D' => 5,
          'E' => 4,
          'F' => 3,
          'G' => 2,
          'H' => 1,
      ];
      
      // expect "no type ..." at bottom, ..., C, C, A, A, B, B
      $priorities = [
          'A' => -6.5,
          'B' => -7,
          'C' => -6,
          'D' => -5,
          'E' => -4,
          'F' => -3,
          'G' => -2,
          'H' => -1,
      ];
      

      谢谢@vivek_23 :)

      【讨论】:

        【解决方案4】:

        您有 3 个用于排序的逻辑规则。 spaceship 运算符提供了非常清晰简洁的语法,没有混乱的条件块。

        只需在 3 路比较运算符 (&lt;=&gt;) 的两边写上相应的元素,就会从左到右遵守规则。当$b 是第一个/顶部/左侧数组时,则应用 DESC 顺序;相反,first/top/left 数组中的$a 等同于 ASC 排序。

        1. 按目标列 DESC 的存在排序
        2. 然后打破 #1 关系,按优先级值 DESC 排序
        3. 然后打破 #2 关系,按目标列值 ASC 排序

        代码:(Demo)

        function arraySortPriority(array &$array, $column, array $priorities):void
        {
            usort($array, function($a, $b) use ($column, $priorities) {
                $aFocus = $a[$column] ?? null;
                $bFocus = $b[$column] ?? null;
                return [array_key_exists($column, $b), $priorities[$bFocus] ?? 0, $aFocus]
                       <=>
                       [array_key_exists($column, $a), $priorities[$aFocus] ?? 0, $bFocus];
            });
        }
        
        arraySortPriority($array, 'type', $priorities);
        var_export($array)
        

        如果你的$array 永远不会包含null 值,那么这个sn-p 可以进一步细化为在usort() 范围内不包含任何函数调用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-09
          • 2012-06-07
          相关资源
          最近更新 更多