【问题标题】:How to sort a PHP array with key/value pair by custom defined order of the key value? [closed]如何通过键值的自定义顺序对具有键/值对的 PHP 数组进行排序? [关闭]
【发布时间】:2021-08-06 01:56:32
【问题描述】:

我一直在寻找各种排序功能,但完全找不到我要找的东西。

这是我当前的数组结果。

  0 =>
  array(
    'member_reference_identifier' => '001',
    'user_status' => 'Hold',
    'is_primary' => 'true',
  ),
  1 =>
  array(
    'member_reference_identifier' => '002',
    'user_status' => 'Banned',
    'is_primary' => 'true',
  ),
  2 =>
  array(
    'member_reference_identifier' => '003',
    'user' => 'Banned',
    'is_primary' => 'true',
  ),
  3 =>
  array(
    'member_reference_identifier' => '004',
    'user_status' => 'Active',
    'is_primary' => 'false',
  ),
  4 =>
  array(
    'member_reference_identifier' => '005',
    'user_status' => 'Active',
    'is_primary' => 'true',
  ),
  5 =>
  array(
    'member_reference_identifier' => '006',
    'user_status' => 'Hold',
    'is_primary' => 'true',
  ),
  6 =>
  array(
    'member_reference_identifier' => '007',
    'user_status' => 'Banned',
    'is_primary' => 'true',
  ),
)

我希望首先显示主要结果,并且我希望有排序顺序:活动 > 保留 > 禁止。

这是我所期待的:

  4 =>
  array(
    'member_reference_identifier' => '005',
    'user_status' => 'Active',
    'is_primary' => 'true',
  ),

  0 =>
  array(
    'member_reference_identifier' => '001',
    'user_status' => 'Hold',
    'is_primary' => 'true',
  ),
  5 =>
  array(
    'member_reference_identifier' => '006',
    'user_status' => 'Hold',
    'is_primary' => 'true',
  ),
  1 =>
  array(
    'member_reference_identifier' => '002',
    'user_status' => 'Banned',
    'is_primary' => 'true',
  ),
  2 =>
  array(
    'member_reference_identifier' => '003',
    'user_status' => 'Banned',
    'is_primary' => 'true',
  ),

  6 =>
  array(
    'member_reference_identifier' => '007',
    'user_status' => 'Banned',
    'is_primary' => 'true',
  ),
  3 =>
  array(
    'member_reference_identifier' => '004',
    'user_status' => 'Active',
    'is_primary' => 'false',
  ),

)

尽管最后一个对象是活动的,但它最终还是因为 is_primary 为假。

添加更多细节以使问题更加集中:我能够确定将我的主阵列分成多个阵列的超级蛮力解决方案。将它们单独排序并再次加入它们。但是,考虑到我正在处理的大型数据集,可能是一个数组中的数百万个对象,这是一个非常昂贵的操作。我主要是在寻找最佳的东西。

【问题讨论】:

  • 这能回答你的问题吗? How to Sort Multi-dimensional Array by Value?
  • @biesior 在发布之前看到了这个问题。该用户(2010 年)正在寻找升序排序。我想要定制订单。
  • 那么你尝试了什么?你应该告诉我们,所以我们不需要猜测,如果usort()不满足你,你也可以迭代输入数组,将元素放入两个新数组,如if ($item['is_primary']===true){$primares[] =$item;} else {$nonPrimaries[]=$item;},最后合并这些数组得到最后一个$finalArray = array_merge($primaries, $nonPrimaries);(对不起,你应该为你的状态创建 3 个数组并合并它们,我的错,但这只是 POC)
  • 老实说,这是个好主意。但是在合并之前,我将不得不为 user_status 分别对这两个数组进行排序。你不觉得太贵了吗?
  • 您可以先对输入数组进行排序,然后再进行拆分+重新合并,这样您就可以将其按一个索引排序并按另一个索引分组。这实际上取决于输入数组的大小,但是对于如此短的数据集,我不会关心性能。我在我的一个项目中使用了最多 100 个元素进行分组,并且没有注意到这种方法有任何性能下降。

标签: php arrays laravel sorting multidimensional-array


【解决方案1】:

尝试uasort - 它将使用用户定义的比较函数对数组进行排序并保持索引关联:

<?php
        $x = [
        0 =>[
                'member_reference_identifier' => '001',
                'user_status' => 'Hold',
                'is_primary' => 'true',
                ],
        1 => [
                'member_reference_identifier' => '002',
                'user_status' => 'Banned',
                'is_primary' => 'true',
                ],
        2 => [
                'member_reference_identifier' => '003',
                'user_status' => 'Banned',
                'is_primary' => 'true',
                ],
        3 =>[
                'member_reference_identifier' => '004',
                'user_status' => 'Active',
                'is_primary' => 'false',
                ],
        4 => [
                'member_reference_identifier' => '005',
                'user_status' => 'Active',
                'is_primary' => 'true',
                ],
        5 => [
                'member_reference_identifier' => '006',
                'user_status' => 'Hold',
                'is_primary' => 'true',
        ],
        6 => [
                'member_reference_identifier' => '007',
                'user_status' => 'Banned',
                'is_primary' => 'true',
                ],
        ];


        function sorter($a,$b){
                $states = ['Active'=>0,'Hold'=>1,'Banned'=>2];
                if ($a['is_primary'] == 'true' && $b['is_primary']=='false') return -1;
                if ($a['is_primary'] == 'false' && $b['is_primary']=='true') return 1;
                return $states[$a['user_status']]<=>$states[$b['user_status']];
        }
        uasort($x,'sorter');
        print_r($x);

PS 你的数组不一致 - 我已经统一了密钥useraccount_statususer_status

【讨论】:

  • 谢谢。 is_primary 的逻辑可以包含在其中吗?或者我应该对 is_primary 键的 user_status 数组排序?
  • 同意!这个答案很棒,基本上是我想要的。如果@pQd 也可以修改它以包含 is_primary 标志,我会接受它作为完整答案。
  • @creamCheeseCoder 我已经更新了我的答案,将 is_primary 作为主要排名因素
  • 这太棒了!谢谢。我可以接受这个作为答案。
  • 但是这样非主成员不会排在数组的末尾。最好将状态设置为 1,2,3,然后在 比较布尔 is_primary 条件之前乘以 1 或 - 1,以便非主成员在数组末尾也得到排序,并且使用更简单和更快的代码。检查我的答案。
【解决方案2】:

使用 pQd 的排序功能的稍微修改版本将给你你想要的。 首先,我们需要通过将值 1 和 false -1 来合并 is_primary 字段值,然后将其与状态数组中的两个值相乘,然后将结果返回给“uasort”函数。我们还需要修改 states 数组,将其向右移一位以消除零,因为它在乘以负数或正数时总是为零。
我没有对此进行测试,但如果没有拼写错误,它应该可以工作。

    function sorter($a,$b){
       $is_primary = ['true' =>-1, 'false' =>1]   // using true =1, false=-1 will reverse is_primary order 
       $states = ['Active'=>1,'Hold'=>2,'Banned'=>3];
       $c=$is_primary[$a['is_primary']];
       $d=$is_primary[$b['is_primary']];
       $e=$states[$a['user_status']];
       $f=$states[$b['user_status']];
      return (e*$c)<=>(f*$d);
    }
    uasort($the_array,'sorter');
    print_r($the_array);     

上面和下面一样。为了更好的可读性和理解,我对其进行了编辑:

    function sorter($a,$b){

      $is_primary = ['true' =>-1, 'false' =>1]       

      $states = ['Active'=>1,'Hold'=>2,'Banned'=>3];

      return $states[$a['user_status']]*$is_primary[$a['is_primary']] <=>$states[$b['user_status']]*$is_primary[$b['is_primary']];

    }

    uasort($the_array,'sorter');

    print_r($the_array);     
     

请注意,那些 is_primary = false 将在状态条件顺序方面颠倒,但这应该很容易在排序器函数内部修复,例如在 IF 语句中初始化数组,每当 $ 的两个 is_primary 字段时反转值a 和 $b 是假的 1,2,3 到 3,2,1 但我不确定,你需要检查 uasort 函数的底层排序算法。

【讨论】:

  • 传递给分拣机的 $a 和 $b 是什么?
  • 你应该先问什么是分拣机! sorter 是 uasort 函数的回调函数,$a 是数组中的一个元素,它与另一个元素 $b 进行比较,它的调用次数取决于 uasort 函数内部使用的排序算法。每次第一次使用内置函数时至少应该看一下文档,并且在开始任何认真的编码之前应该学习排序算法的主题
  • 参考这个en.wikipedia.org/wiki/Merge_sort 和这个php.net/manual/en/function.uasort.php 和这个php.net/manual/en/function.usort.php 并参考关于 运算符的文档,其余的是关于当我们将数字与相同的相乘时会发生什么的基本数学或不同的符号正负和零
【解决方案3】:

你可以使用集合的 sortBy 方法。

$array = array(
  0 =>
  array(
    'member_reference_identifier' => '001',
    'user_status' => 'Hold',
    'is_primary' => 'true',
  ),
  1 =>
  array(
    'member_reference_identifier' => '002',
    'user_status' => 'Banned',
    'is_primary' => 'true',
  ),
  2 =>
  array(
    'member_reference_identifier' => '003',
    'user' => 'Banned',
    'is_primary' => 'true',
  ),
  3 =>
  array(
    'member_reference_identifier' => '004',
    'user_status' => 'Active',
    'is_primary' => 'false',
  ),
  4 =>
  array(
    'member_reference_identifier' => '005',
    'user_status' => 'Active',
    'is_primary' => 'true',
  ),
  5 =>
  array(
    'member_reference_identifier' => '006',
    'user_status' => 'Hold',
    'is_primary' => 'true',
  ),
  6 =>
  array(
    'member_reference_identifier' => '007',
    'user_status' => 'Banned',
    'is_primary' => 'true',
  ),
);
collect($array)
    ->sortBy(function ($item) {
        if (!isset($item['user_status'] || !isset($item['is_primary'])) {
            return 7;
        } elseif ($item['user_status'] == 'Active' && $item['is_primary'] == 'true') {
            return 1;
        } elseif ($item['user_status'] == 'Hold' && $item['is_primary'] == 'true') {
            return 2;
        } elseif ($item['user_status'] == 'Banned' && $item['is_primary'] == 'true') {
            return 3;
        } elseif ($item['user_status'] == 'Active' && $item['is_primary'] == 'false') {
            return 4;
        } elseif ($item['user_status'] == 'Hold' && $item['is_primary'] == 'false') {
            return 5;
        } elseif ($item['user_status'] == 'Banned' && $item['is_primary'] == 'false') {
            return 6;
        } else {
            return 7;
        }
    })
    ->values()
    ->all();

我在闭包中所做的是为每种可能性分配一个数字。这个数字就是 sortBy 方法用来对数据进行排序的数字。


您也可以使用 LazyCollections 代替 Collections。大部分逻辑保持不变。

use Illuminate\Support\LazyCollection;

$sorted = LazyCollection::make($array)->sortBy(function ($item) {
        ...
    })
    ->values()
    ->all();

【讨论】:

  • 感谢您的回答,但我正在寻找更便宜的解决方案,因为我无法在 100 万加对象数组/集合上使用 if_else。
  • 在这种情况下,您可以使用 LazyCollections。他们使用很少的内存。我将在答案的末尾添加一个部分。
猜你喜欢
  • 2017-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-08
  • 2015-04-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多