问题
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):
- 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).
- 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