PHP How to sort an array of objects based on priority order with a backup of alphabetical?

旧巷老猫 提交于 2021-02-04 08:39:27

问题


I have an array like this:

$sort_me = array(
   array("file"=>"Desert.jpg"), 
   array("file"=>"Hello.jpg"), 
   array("file"=>"Test.jpg)
)

I want to sort this array based on the file attribute alphabetically. To complicate matters I have an optional array:

$sort_order = array("Test.jpg", "Hello.jpg", "NotFound.jpg")

This array will specify some user defined ordering. These array elements have priority, and will be placed in that order if they are found. Other elements not matching anything in $sort_order should be ordered at the end of the array alphabetically.

In javascript I would use call a sort function with a comparator function that takes 2 elements and returns a number to place the first object ahead or behind the second.

How can I do this with PHP?

Edit

I attempted something and it didn't work. (Modified again to put the logic into one function, and generalize which field to sort by)

<?php
    function sort_priority($sort_me, $sort_order, $field) {
        function compare($a, $b) {
            if ($sort_order) {
                $ai = array_search($a[$field], $sort_order);
                $bi = array_search($b[$field], $sort_order);
                if ($ai !== false && $bi === false) {
                    return -1;
                } else if ($bi !== false && $ai === false) {
                    return 1;
                } else if ($ai !== false && $bi !== false) {
                    return $bi - $ai;
                }
            }
            return $a[$field] < $b[$field] ? -1 : 1;
        }
        usort($sort_me, "compare");
    }

    $sort_order = array("Test.jpg", "Hello.jpg", "NotFound.jpg");
    $sort_me = array(
       array("file"=>"Test.jpg"),
       array("file"=>"Desert.jpg"),
       array("file"=>"Hello.jpg")
    );

    sort_priority($sort_me, $sort_order, "file");
    echo json_encode($sort_me);
?>

This outputs

 Notice: Undefined variable: sort_order in c:\workspace\test.php on line 10

The expected output is

[{"file":"Test.jpg"},{"file":"Hello.jpg"},{"file":"Desert.jpg"}]

I don't know how to get the compare function to properly use the context specific $sort_order function.

Edit

I accepted an answer, but for completeness I wanted to post what I finally ended up with that seemed to work. If anyone wants to post a more elegant solution, I would consider marking it as accepted. But here is what I have:

<?php
    function compare_priority($a, $b) {
        global $g_order, $g_field;
        if ($g_order) {
            $ai = array_search($a[$g_field], $g_order);
            $bi = array_search($b[$g_field], $g_order);
            if ($ai !== false && $bi === false) {
                return -1;
            } else if ($bi !== false && $ai === false) {
                return 1;
            } else if ($ai !== false && $bi !== false) {
                return $ai - $bi;
            }
        }
        return $a[$g_field] < $b[$g_field] ? -1 : 1;
    }

    function sort_priority(&$sort_me, $sort_order, $field) {
        global $g_order, $g_field;
        $g_order = $sort_order;
        $g_field = $field;
        usort($sort_me, "compare_priority");
    }

    $sort_me = array(
       array("file"=>"Z"), 
       array("file"=>"A"), 
       array("file"=>"Y"), 
       array("file"=>"B")
    );
    $sort_order = array("Z", "Y", "C");
    sort_priority($sort_me, $sort_order, "file");
    echo json_encode($sort_me);
?>

回答1:


You can do the same as you would have done with javascript. usort (http://ch2.php.net/manual/en/function.usort.php) allow you to define your custom comparison between elements.

I modified your code and it seems to work :

<?php
    function test() {
        $sort_me = array(
           array("file"=>"Test.jpg"),
           array("file"=>"Desert.jpg"),
           array("file"=>"Hello.jpg")
        );
        global $sort_order;
        $sort_order = array("Test.jpg" , "Hello.jpg", "NotFound.jpg");
        function compare($a, $b) {
            global $sort_order;
            if (is_array($sort_order)) {
                $ai = array_search($a["file"], $sort_order);
                $bi = array_search($b["file"], $sort_order);
                if ($ai !== false && $bi === false) {
                    return -1;
                } else if ($bi !== false && $ai === false) {
                    return 1;
                } else if ($ai !== false && $bi !== false) {
                    return $ai - $bi;
                }
            }
            return $a["file"] < $b["file"] ? -1 : 1;
        }
        usort($sort_me, "compare");
        echo json_encode($sort_me);
    }

    test();
?>



回答2:


For the cleanest scripting with fewer function calls, use usort() with the spaceship operator.

My technique to follow obeys the 2-step logic (the 1st step containing a condition):

  1. sort by the priority array's index values; if the value is not in the priority array, then use the fallback value (which is higher than the last element's key).
  2. when breaking ties that result from the first rule, sort alphabetically

Sample Inputs:

$sort_me = [
   ["file" => "Desert.jpg"], 
   ["file" => "What.jpg"], 
   ["file" => "Hello.jpg"], 
   ["file" => "Test.jpg"],
   ["file" => "Goodness.jpg"],
];

$sort_order = ["Test.jpg", "Hello.jpg", "NotFound.jpg"];

Processing Code: (Demo)

$lookup = array_flip($sort_order);
$fallback = count($sort_order);

usort($sort_me, function($a, $b) use ($lookup, $fallback) {
    return [$lookup[$a['file']] ?? $fallback, $a['file']]
           <=>
           [$lookup[$b['file']] ?? $fallback, $b['file']];
});
var_export($sort_me);

Output:

array (
  0 => 
  array (
    'file' => 'Test.jpg',
  ),
  1 => 
  array (
    'file' => 'Hello.jpg',
  ),
  2 => 
  array (
    'file' => 'Desert.jpg',
  ),
  3 => 
  array (
    'file' => 'Goodness.jpg',
  ),
  4 => 
  array (
    'file' => 'What.jpg',
  ),
)

From PHP7.4, you can use arrow function syntax to introduce global variables into the custom function's scope without the use() declaration.

usort($sort_me, fn($a, $b) =>
    [$lookup[$a['file']] ?? $fallback, $a['file']]
    <=>
    [$lookup[$b['file']] ?? $fallback, $b['file']]
);


来源:https://stackoverflow.com/questions/21068047/php-how-to-sort-an-array-of-objects-based-on-priority-order-with-a-backup-of-alp

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!