【问题标题】:How to sort an array of associative arrays by value of a given key in PHP?如何按PHP中给定键的值对关联数组的数组进行排序?
【发布时间】:2010-12-08 12:54:21
【问题描述】:

给定这个数组:

$inventory = array(

   array("type"=>"fruit", "price"=>3.50),
   array("type"=>"milk", "price"=>2.90),
   array("type"=>"pork", "price"=>5.43),

);

我想按价格对$inventory的元素进行排序得到:

$inventory = array(

   array("type"=>"pork", "price"=>5.43),
   array("type"=>"fruit", "price"=>3.50),
   array("type"=>"milk", "price"=>2.90),

);

我该怎么做?

【问题讨论】:

标签: php arrays sorting multidimensional-array


【解决方案1】:

你是对的,你要找的函数是array_multisort()

以下是直接取自手册并根据您的情况改编的示例:

$price = array();
foreach ($inventory as $key => $row)
{
    $price[$key] = $row['price'];
}
array_multisort($price, SORT_DESC, $inventory);

从 PHP 5.5.0 开始,您可以使用 array_column() 代替 foreach:

$price = array_column($inventory, 'price');

array_multisort($price, SORT_DESC, $inventory);

【讨论】:

  • 虽然这肯定比替代品贵。
  • 更贵?这很奇怪,在我的机器上(运行 PHP 5.3.1-dev)array_multisort() 在小数组上快了几个百分点,在大数组(100 多个元素)上快了 100 倍
  • 使用数字键不需要任何更改。如果您遇到与数字键相关的错误或奇怪行为,请将其作为新问题发布。
  • array_multisort 有一个大问题:它不维护原始键。
  • @machineaddict 它确实维护关联键。
【解决方案2】:

PHP 7+

从 PHP 7 开始,这可以通过使用 usort 和使用 spaceship operator 比较元素的 anonymous function 来简洁地完成。

你可以像这样进行升序排序:

usort($inventory, function ($item1, $item2) {
    return $item1['price'] <=> $item2['price'];
});

或者像这样的降序:

usort($inventory, function ($item1, $item2) {
    return $item2['price'] <=> $item1['price'];
});

要了解其工作原理,请注意usort 采用用户提供的比较函数,该函数的行为必须如下(来自文档):

如果认为第一个参数分别小于、等于或大于第二个参数,则比较函数必须返回一个小于、等于或大于零的整数。

还要注意&lt;=&gt;,宇宙飞船操作员,

如果两个操作数相等则返回 0,如果左边更大则返回 1,如果右边更大则返回 -1

这正是usort 所需要的。事实上,将&lt;=&gt; 添加到https://wiki.php.net/rfc/combined-comparison-operator 中的语言的几乎全部理由是它

使编写用于usort() 的排序回调更容易


PHP 5.3+

PHP 5.3 引入了匿名函数,但还没有 spaceship 运算符。我们仍然可以使用usort 对我们的数组进行排序,但它有点冗长且难以理解:

usort($inventory, function ($item1, $item2) {
    if ($item1['price'] == $item2['price']) return 0;
    return $item1['price'] < $item2['price'] ? -1 : 1;
});

请注意,尽管处理整数值的比较器通常只返回值的差值,例如 $item2['price'] - $item1['price'],但在这种情况下我们不能安全地这样做。这是因为在提问者的示例中价格是浮点数,但我们传递给usort 的比较函数必须返回整数,usort 才能正常工作:

从比较函数返回非整数值,例如浮点数,将导致回调返回值的内部强制转换为整数。因此 0.99 和 0.1 等值都将被转换为整数值 0,这将比较这些值是否相等。

这是在 PHP 5.x 中使用 usort 时要牢记的一个重要陷阱! My original version of this answer 犯了这个错误,但我显然在没有人注意到严重错误的情况下,对数千个视图累积了十个赞成票。像我这样的笨蛋可以轻松搞砸比较器函数正是在 PHP 7 中将更易于使用的宇宙飞船运算符添加到语言中的原因。

【讨论】:

  • 抱歉,这种方法会从关联数组中删除字符串键。应该使用“uasort”函数。
  • @DotMat 有趣 - 我不知道 uasort。但是,在查看文档后,这个答案仍然是正确的在这种情况下。在 OP 的示例中,要排序的数组具有顺序数字索引而不是字符串索引,因此 usort 更合适。在按顺序索引的数组上使用uasort 将导致排序后的数组不按其数字索引排序,因此foreach 循环中看到的第一个元素不是$your_array[0],这不太可能是理想的行为.
【解决方案3】:

虽然其他人正确建议使用array_multisort(),但由于某种原因,似乎没有答案承认array_column() 的存在,这可以大大简化解决方案。所以我的建议是:

array_multisort(array_column($inventory, 'price'), SORT_DESC, $inventory);

如果你想对字符串进行不区分大小写的排序,你可以使用SORT_NATURAL|SORT_FLAG_CASE

array_multisort(array_column($inventory, 'key_name'), SORT_DESC, SORT_NATURAL|SORT_FLAG_CASE, $inventory);

【讨论】:

  • 由于某种原因,我无法使用具有小写/大写字母的字符串。即使使用 SORT_FLAG_CASE。以下对我来说适用于字符串比较:array_multisort(array_map(strtolower, array_column($ipr_projects, 'Name')), SORT_ASC, $ipr_projects);
【解决方案4】:

由于您的数组元素本身就是带有字符串键的数组,因此最好的办法是定义一个自定义比较函数。这是非常快速和容易做到的。试试这个:

function invenDescSort($item1,$item2)
{
    if ($item1['price'] == $item2['price']) return 0;
    return ($item1['price'] < $item2['price']) ? 1 : -1;
}
usort($inventory,'invenDescSort');
print_r($inventory);

产生以下内容:

Array
(
    [0] => Array
        (
            [type] => pork
            [price] => 5.43
        )

    [1] => Array
        (
            [type] => fruit
            [price] => 3.5
        )

    [2] => Array
        (
            [type] => milk
            [price] => 2.9
        )

)

【讨论】:

  • 结合这里的一些其他 cmets(uasort 和内联匿名函数),你会得到这个单行:uasort( $inventory, function ($a, $b) { if ( $a==$b ) return 0; else return ($a &gt; $b) ? -1 : 1; });
  • @AlanPorter usort 似乎比 uasort 更适合使用顺序数字键对数组进行排序。以第一个元素位于索引1 而第二个元素位于索引0 的数组结尾是奇怪的行为,对于不熟悉PHP 数组细节的人来说无疑是一个陷阱; usort 为您提供直观期望的输出。
【解决方案5】:

我就这样结束了:

function sort_array_of_array(&$array, $subfield)
{
    $sortarray = array();
    foreach ($array as $key => $row)
    {
        $sortarray[$key] = $row[$subfield];
    }

    array_multisort($sortarray, SORT_ASC, $array);
}

只需调用函数,传递数组和二级数组的字段名称。 喜欢:

sort_array_of_array($inventory, 'price');

【讨论】:

  • 不同意...我没有说这是一个不同的解决方案,我只是说我以这个解决方案结束并给出了完整的工作功能。
  • @MarkAmery 我更喜欢函数中包含的答案。它鼓励复制粘贴使用函数,并希望编写更少的意大利面条代码。
【解决方案6】:

您可以将usort 与匿名函数一起使用,例如

usort($inventory, function ($a, $b) { return strnatcmp($a['price'], $b['price']); });

【讨论】:

  • 版本 PHP 5 >= 5.5.0, PHP 7 适合像我这样真正希望它为他们工作的人..
  • 值得注意的是,用于比较字符串的strnatcmp 在这里似乎工作正常。显然,它实现的“自然顺序”包括按数字而非词法对数字字符串进行排序。
【解决方案7】:

来自Sort an array of associative arrays by value of given key in php

通过使用 usort (http://php.net/usort) ,我们可以对数组进行升序和降序排序。只是我们需要创建一个函数并将其作为参数传递给 usort。根据下面的示例,如果我们通过小于条件,则大于用于升序,则它按降序排序。 示例:

$array = array(
  array('price'=>'1000.50','product'=>'test1'),
  array('price'=>'8800.50','product'=>'test2'),
  array('price'=>'200.0','product'=>'test3')
);

function cmp($a, $b) {
  return $a['price'] > $b['price'];
}

usort($array, "cmp");
print_r($array);

输出:

Array
 (
    [0] => Array
        (
            [price] => 200.0
            [product] => test3
        )

    [1] => Array
        (
            [price] => 1000.50
            [product] => test1
        )

    [2] => Array
        (
            [price] => 8800.50
            [product] => test2
        )
  )

【讨论】:

  • 这个答案出现在低质量审查队列中,大概是因为您没有提供任何代码解释。如果此代码回答了问题,请考虑在答案中添加一些解释代码的文本。这样一来,您就更有可能获得更多支持,并帮助提问者学习新知识。
  • -1;这里的cmp 函数是错误的。它应该返回“如果第一个参数被认为分别小于、等于或大于第二个参数,则该整数小于、等于或大于零”,而是返回@987654326 @ 或 false。值得注意的是,它似乎仍然有效——也许是因为usort 和朋友的当前实现对“小于”和“等于”情况的处理方式相同——但不要指望它在未来的 PHP 版本中继续工作。如果他们试图使排序保持稳定(即不要不必要地移动相等的元素),这将会中断。
  • 另外,usort 在这里比uasort 更合适,因为uasort 保留了键和值之间的关联,这在处理顺序数字数组时会令人困惑和意外。比如上面$array调用uasort后的索引依次是2、0、1。除非您出于某种原因想要这样做,否则使用 usort 可能会更舒服,它会重新索引数组并对其进行重新排序。
  • 在 php7+ 中:在 cmp 函数中应该使用 'spaceship' 运算符
【解决方案8】:
$inventory = 
    array(array("type"=>"fruit", "price"=>3.50),
          array("type"=>"milk", "price"=>2.90),
          array("type"=>"pork", "price"=>5.43),
          );

function pricesort($a, $b) {
  $a = $a['price'];
  $b = $b['price'];
  if ($a == $b)
    return 0;
  return ($a > $b) ? -1 : 1;
}

usort($inventory, "pricesort");
// uksort($inventory, "pricesort");

print("first: ".$inventory[0]['type']."\n\n");
// for usort(): prints milk (item with lowest price)
// for uksort(): prints fruit (item with key 0 in the original $inventory)

// foreach prints the same for usort and uksort.
foreach($inventory as $i){
  print($i['type'].": ".$i['price']."\n");
}

输出:

first: pork

pork: 5.43
fruit: 3.5
milk: 2.9

【讨论】:

    【解决方案9】:

    我像这样使用uasort

    <?php
    $users = [
        [
            'username' => 'joe',
            'age' => 11
        ],
        [
            'username' => 'rakoto',
            'age' => 21
        ],
        [
            'username' => 'rabe',
            'age' => 17
        ],
        [
            'username' => 'fy',
            'age' => 19
        ],    
    ];
    
    
    uasort($users, function ($item, $compare) {
        return $item['username'] >= $compare['username']; 
    });
    
    var_dump($users);
    

    【讨论】:

    • 纯代码答案在 Stack Overflow 上的价值很低,因为它们在教育/授权 OP 和成千上万的未来研究人员方面做得很差。此外,建议多年前已经提供的技术对研究人员没有帮助 - 事实上,这会浪费他们的研究时间,因为他们最终会阅读更多内容但没有获得新信息。
    【解决方案10】:

    已对 100 000 条记录进行了测试: 以秒为单位的时间(由函数 microtime 计算)。 仅用于排序关键位置的唯一值。

    @Josh Davis 的功能解决方案: 花费时间:1.5768740177155

    矿山解决方案: 花费时间:0.094044923782349

    解决方案:

    function SortByKeyValue($data, $sortKey, $sort_flags=SORT_ASC)
    {
        if (empty($data) or empty($sortKey)) return $data;
    
        $ordered = array();
        foreach ($data as $key => $value)
            $ordered[$value[$sortKey]] = $value;
    
        ksort($ordered, $sort_flags);
    
        return array_values($ordered); *// array_values() added for identical result with multisort*
    }
    

    【讨论】:

    • 但是,对唯一排序键的要求有点破坏交易。如果您有可以作为键的唯一排序值,这就引出了一个问题:为什么不简单地用这些键构造数组呢?在 OP 的场景中,很难想象两个价格相同的商品是不可能的。请记住,使用此解决方案会导致数组中的项目从排序的结果集中神秘而无声地消失。
    • @Chris Baker,你是对的。这仅适用于唯一值。但是这个解决方案工作得非常快,所以速度是制造和使用它的原因。目前可能不实际,需要用 PHP 7.1.x 进行测试。
    【解决方案11】:

    适用于 PHP 7 及更高版本。

    /**
     * A method for sorting associative arrays by a key and a direction.
     * Direction can be ASC or DESC.
     *
     * @param $array
     * @param $key
     * @param $direction
     * @return mixed $array
     */
    function sortAssociativeArrayByKey($array, $key, $direction){
    
        switch ($direction){
            case "ASC":
                usort($array, function ($first, $second) use ($key) {
                    return $first[$key] <=> $second[$key];
                });
                break;
            case "DESC":
                usort($array, function ($first, $second) use ($key) {
                    return $second[$key] <=> $first[$key];
                });
                break;
            default:
                break;
        }
    
        return $array;
    }
    

    用法:

    $inventory = sortAssociativeArrayByKey($inventory, "price", "ASC");
    

    【讨论】:

      【解决方案12】:

      试试这个:

      $prices = array_column($inventory, 'price');
      array_multisort($prices, SORT_DESC, $inventory);
      print_r($inventory);
      

      【讨论】:

      • 您好,欢迎来到 stackoverflow,感谢您的回答。虽然这段代码可能会回答这个问题,但您是否可以考虑添加一些解释来说明您解决了什么问题,以及您是如何解决的?这将有助于未来的读者更好地理解您的答案并从中学习。
      • 请不要重复以前帖子的建议(尤其是在同一页面上)。这会不必要地膨胀 Stack Overflow 并浪费研究人员的时间。
      【解决方案13】:

      此功能可重复使用:

      function usortarr(&$array, $key, $callback = 'strnatcasecmp') {
          uasort($array, function($a, $b) use($key, $callback) {
              return call_user_func($callback, $a[$key], $b[$key]);
          });
      }
      

      默认情况下,它适用于字符串值,但如果您的所有值都是数字,则您必须为 number comparison function 子回调。

      【讨论】:

      • 你调用这个usortarr,然后调用uasort而不是usort;也许有点混乱。后者是 - 在具有数字索引的顺序数组的情况下,就像问题中展示的那样 - 可能是你真正想要的。
      【解决方案14】:

      您可以尝试定义自己的比较函数,然后使用usort

      【讨论】:

      • 是的。如果我找不到解决方案,我会这样做。我很确定有一些奇怪的参数可以添加到其中一种来实现这一点。不过感谢您的想法!
      • 与此处发布的其他答案相比,此答案看起来更像是一个“提示”,应该是问题下的评论而不是答案。
      【解决方案15】:

      这是我很久以前发现并清理了一下的方法。这很好用,并且可以快速更改为接受对象。

      /**
       * A method for sorting arrays by a certain key:value.
       * SortByKey is the key you wish to sort by
       * Direction can be ASC or DESC.
       *
       * @param $array
       * @param $sortByKey
       * @param $sortDirection
       * @return array
       */
      private function sortArray($array, $sortByKey, $sortDirection) {
      
          $sortArray = array();
          $tempArray = array();
      
          foreach ( $array as $key => $value ) {
              $tempArray[] = strtolower( $value[ $sortByKey ] );
          }
      
          if($sortDirection=='ASC'){ asort($tempArray ); }
              else{ arsort($tempArray ); }
      
          foreach ( $tempArray as $key => $temp ){
              $sortArray[] = $array[ $key ];
          }
      
          return $sortArray;
      
      }
      

      要更改对对象进行排序的方法,只需更改以下行:

      $tempArray[] = strtolower( $value[ $sortByKey ] );$tempArray[] = strtolower( $value-&gt;$sortByKey );

      要运行该方法,只需这样做

      sortArray($inventory,'price','ASC');

      【讨论】:

      • 这种方法有效,但比 Josh Davis 的答案(array_multisort)或我的答案(usort)略逊一筹,而且似乎没有比他们提供任何优势作为交换。
      【解决方案16】:
      //Just in one line custom function
      function cmp($a, $b)
      {
      return (float) $a['price'] < (float)$b['price'];
      }
      @uasort($inventory, "cmp");
      print_r($inventory);
      
      //result
      
      Array
      (
      [2] => Array
          (
              [type] => pork
              [price] => 5.43
          )
      
      [0] => Array
          (
              [type] => fruit
              [price] => 3.5
          )
      
      [1] => Array
          (
              [type] => milk
              [price] => 2.9
          )
      
      )
      

      【讨论】:

        【解决方案17】:

        完整的动态函数 我跳到这里进行关联数组排序,并在http://php.net/manual/en/function.sort.php 上发现了这个惊人的功能。这个函数是非常动态的,可以通过指定的key进行升序和降序排序。

        按特定键对数组进行排序的简单函数。维护索引关联

        <?php
        
        function array_sort($array, $on, $order=SORT_ASC)
        {
            $new_array = array();
            $sortable_array = array();
        
            if (count($array) > 0) {
                foreach ($array as $k => $v) {
                    if (is_array($v)) {
                        foreach ($v as $k2 => $v2) {
                            if ($k2 == $on) {
                                $sortable_array[$k] = $v2;
                            }
                        }
                    } else {
                        $sortable_array[$k] = $v;
                    }
                }
        
                switch ($order) {
                    case SORT_ASC:
                        asort($sortable_array);
                    break;
                    case SORT_DESC:
                        arsort($sortable_array);
                    break;
                }
        
                foreach ($sortable_array as $k => $v) {
                    $new_array[$k] = $array[$k];
                }
            }
        
            return $new_array;
        }
        
        $people = array(
            12345 => array(
                'id' => 12345,
                'first_name' => 'Joe',
                'surname' => 'Bloggs',
                'age' => 23,
                'sex' => 'm'
            ),
            12346 => array(
                'id' => 12346,
                'first_name' => 'Adam',
                'surname' => 'Smith',
                'age' => 18,
                'sex' => 'm'
            ),
            12347 => array(
                'id' => 12347,
                'first_name' => 'Amy',
                'surname' => 'Jones',
                'age' => 21,
                'sex' => 'f'
            )
        );
        
        print_r(array_sort($people, 'age', SORT_DESC)); // Sort by oldest first
        print_r(array_sort($people, 'surname', SORT_ASC)); // Sort by surname
        

        【讨论】:

          【解决方案18】:

          如果您需要对不同大小写的字符串数组进行排序,这会将排序数组的值更改为小写。

          $data = [
              [
                  'name' => 'jack',
                  'eyeColor' => 'green'
              ],
              [
                  'name' => 'Amy',
                  'eyeColor' => 'brown'
              ],
              [   
                  'name' => 'Cody',
                  'eyeColor' => 'blue'
              ] 
          ];
          function toLowerCase($a) { return strtolower($a); }
          $sortArray = array_map("toLowerCase",array_column($data, 'name'));
          array_multisort($sortArray, SORT_ASC, $data);
          

          【讨论】:

            【解决方案19】:
            $arr1 = array(
            
                array('id'=>1,'name'=>'aA','cat'=>'cc'),
                array('id'=>2,'name'=>'aa','cat'=>'dd'),
                array('id'=>3,'name'=>'bb','cat'=>'cc'),
                array('id'=>4,'name'=>'bb','cat'=>'dd')
            );
            
            $result1 = array_msort($arr1, array('name'=>SORT_DESC);
            
            $result2 = array_msort($arr1, array('cat'=>SORT_ASC);
            
            $result3 = array_msort($arr1, array('name'=>SORT_DESC, 'cat'=>SORT_ASC));
            
            
            function array_msort($array, $cols)
            {
                $colarr = array();
                foreach ($cols as $col => $order) {
                $colarr[$col] = array();
                foreach ($array as $k => $row) { $colarr[$col]['_'.$k] = strtolower($row[$col]); }
            }
            
            $eval = 'array_multisort(';
            
            foreach ($cols as $col => $order) {
                $eval .= '$colarr[\''.$col.'\'],'.$order.',';
            }
            
            $eval = substr($eval,0,-1).');';
            eval($eval);
            $ret = array();
            foreach ($colarr as $col => $arr) {
                foreach ($arr as $k => $v) {
                    $k = substr($k,1);
                    if (!isset($ret[$k])) $ret[$k] = $array[$k];
                    $ret[$k][$col] = $array[$k][$col];
                }
            }
            return $ret;
            
            
            } 
            

            【讨论】:

            • 虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。也请尽量不要用解释性 cmets 挤满你的代码,因为这会降低代码和解释的可读性!
            【解决方案20】:

            试试这个:

            asort($array_to_sort, SORT_NUMERIC);
            

            供参考,请参阅: http://php.net/manual/en/function.asort.php

            在此处查看各种排序标志: http://www.php.net/manual/en/function.sort.php

            【讨论】:

            • 这不适用于多维数组,但只是帮我解决了另一个问题,谢谢:)
            • 这不能用于按特定字典键对字典列表进行排序,因此不能回答提出的问题。
            猜你喜欢
            • 1970-01-01
            • 2016-05-17
            • 1970-01-01
            • 2018-03-12
            • 2020-06-03
            • 2023-04-06
            • 1970-01-01
            • 1970-01-01
            • 2011-07-09
            相关资源
            最近更新 更多