Sort mutidimensional array by values total - php

I am trying to sort multidimensional array by values total in php.
[
c => [1=>22, 2=> 14, 3=> 55],
a => [7=> 33, 2=> 19, 51=> 43, 14=> 27],
...
]
since values total of a subarray are higher than in c, it should be first in this example.
I would appreciate very much help regarding this problem.

You can use array_multisort with array_map
array_multisort(array_map(function($v){return array_sum($v);},$a), SORT_DESC, $a);
First argument with array_map is the sum of the all sub-array. You can change the order of the result array by modifying the 2nd argument SORT_ASC, SORT_DESC
Working example : https://3v4l.org/4fpXh

As #splash58 comment (just to make formal answer):
Use uasort() and array_sum as:
uasort($arr, function($a, $b) { return array_sum($b) - array_sum($a);});
Live example: 3v4l

Related

PHP - Sort multi-dimensional array of number arrays

I have an array:
$arr = [
[4,6],
[1,2,3],
[7,8,9]
];
I'd like to sort it so the result is
$arr = [
[1,2,3],
[4,6],
[7,8,9]
];
If I apply sort($arr), it first sorts by array length and then compares the values. So I get
$arr = [
[4,6],
[7,8,9],
[1,2,3],
];
which is wrong for my purposes.
I could use a sorting algorithm to compare the elements.
Or I could create another array with each element imploded and then sort it.
But was wondering if there was an inbuilt or quicker way of getting this?
Thanks
You can use rsort() to get the order you are expecting.

How to sort an associative array by key type and force specific order in PHP? [duplicate]

This question already has answers here:
Custom key-sort a flat associative based on another array
(16 answers)
Closed 2 years ago.
I have an associative array filled with more or less randomly mixed numeric and string keys, let's go with such example:
$arr = array('third'=>321, 4=>1, 65=>6, 'first'=>63, 5=>88, 'second'=>0);
Now I'd like to sort it in such way:
Sort the array so the numeric keys would be first, and string keys after.
The numeric values sholud be in specific order: 5, 4, 65
The string keys should be ordered by specific order as well: 'first', 'second', 'third'
Output in this case should look like: array(5=>88, 4=>1, 65=>6, 'first'=>63, 'second'=>0, 'third'=>321)
Each element might not be present at all in the original $arr, which might be additional problem...
My guess would be to split the array and make separate one with string keys, and one with numerc keys. Than sort each one and glue them together... But I do't know how to do it?
Edit: Turned out to be a very poor idea, much better approach is in the answer below.
The spaceship operator inside of a uksort() call is the only way that I would do this one.
Set up a two arrays containing your prioritized numeric and word values, then flip them so that they can be used a lookups whereby the respective values dictate the sorting order.
By writing two arrays of sorting criteria separated by <=> the rules will be respected from left to right.
The is_int() check is somewhat counterintuitive. Because we want true outcomes to come before false outcomes, I could have swapped $a and $b in the first element of the criteria arrays, but I decided to keep all of the variables in the same criteria array and just negate the boolean outcome. When sorting ASC, false comes before true because it is like 0 versus 1.
Code: (Demo)
$numericPriorities = array_flip([5, 4, 65]);
$numericOutlier = count($numericPriorities);
$wordPriorities = array_flip(['first', 'second', 'third']);
$wordOutlier = count($wordPriorities);
$arr = ['third' => 321, 4 => 1, 'zero' => 'last of words', 7 => 'last of nums', 65 => 6, 'first' => 63, 5 => 88, 'second' => 0];
uksort(
$arr,
function($a, $b) use ($numericPriorities, $numericOutlier, $wordPriorities, $wordOutlier) {
return [!is_int($a), $numericPriorities[$a] ?? $numericOutlier, $wordPriorities[$a] ?? $wordOutlier]
<=>
[!is_int($b), $numericPriorities[$b] ?? $numericOutlier, $wordPriorities[$b] ?? $wordOutlier];
}
);
var_export($arr);
or (demo)
uksort(
$arr,
function($a, $b) use ($numericPriorities, $numericOutlier, $wordPriorities, $wordOutlier) {
return !is_int($a) <=> !is_int($b)
?: (($numericPriorities[$a] ?? $numericOutlier) <=> ($numericPriorities[$b] ?? $numericOutlier))
?: (($wordPriorities[$a] ?? $wordOutlier) <=> ($wordPriorities[$b] ?? $wordOutlier));
}
);
Output:
array (
5 => 88,
4 => 1,
65 => 6,
7 => 'last of nums',
'first' => 63,
'second' => 0,
'third' => 321,
'zero' => 'last of words',
)
I was extreamly overthinking the case. I came out with a solution that I was unalbe to implement, and unnecessary I described it and ask for help on how to do this.
Turned out that what I really wanted was extreamly simple:
$arr = array('third'=>321, 4=>1, 65=>6, 'first'=>63, 5=>88, 'second'=>0);
$proper_order = array(5, 4, 65, 'first', 'second', 'third');
$newtable = array();
foreach ($proper_order as $order)
{
if (isset($arr[$order]))
$newtable[$order] = $arr[$order];
}
unset($order, $arr, $proper_order);
print_r($newtable);
The result is as expected, and the code should resist the case when the original $arr is incomplete:
Array
(
[5] => 88
[4] => 1
[65] => 6
[first] => 63
[second] => 0
[third] => 321
)

Custom sort an associative array by its keys using a lookup array

I need to sort the following flat, associative array by its keys, but not naturally. I need to sort the keys by a predefined array of values.
$aShips = [
'0_204' => 1,
'0_205' => 2,
'0_206' => 3,
'0_207' => 4
];
My order array looks like this:
$order = ["0_206", "0_205", "0_204", "0_207"];
The desired result:
[
'0_206' => 3,
'0_205' => 2,
'0_204' => 1,
'0_207' => 4
]
I know how to write a custom sorting function, but I don't know how to integrate the order array.
function cmp($a, $b){
if ($a==$b) return 0;
return ($a<$b)?-1:1;
}
uasort($aShips, "cmp");
If you want to sort by keys. Use uksort. Try the code below.
<?php
$aShips = array('0_204' => 1, '0_205' => 2, '0_206' => 3, '0_207' => 4);
uksort($aShips, function($a, $b) {
return $b > $a;
});
According to the Official PHP Docs, you can use uasort() like this (just for demo):
<?php
// Comparison function
function cmp($a, $b) {
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
// Array to be sorted
$array = array('a' => 4, 'b' => 8, 'c' => -1, 'd' => -9, 'e' => 2, 'f' => 5, 'g' => 3, 'h' => -4);
print_r($array);
// Sort and print the resulting array
uasort($array, 'cmp');
print_r($array);
?>
You don't need to leverage a sorting algorithm.
Simply flip $order to use its values as keys (maintaining their order), then merge the two arrays to replace the unwanted values of $order with the values from $aShip.
This assumes that $aShip includes all key values represented in $order, of course. If not, array_intersect_key() can be used to filter $order, but this is dwelling on a fringe case not included in the posted question.
Code: (Demo)
var_export(array_replace(array_flip($order), $aShips));
Output:
array (
'0_206' => 3,
'0_205' => 2,
'0_204' => 1,
'0_207' => 4,
)
This also works when you haven't listed every occurring key in $order -- the unlisted keys are "moved to the back". Proven by this Demo.
Using a custom sorting algorithm can be done and there will be a number of ways, but I'll only show one for comparison's sake.
Reverse your order array and use it as a lookup array while asking uksort() to sort in a descending fashion. If an encountered key is not found in the lookup assign it a value of -1 to ensure it moves to the back of the array.
Code: (Demo)
$lookup = array_flip(array_reverse($order));
uksort($aShips, function($a, $b) use ($lookup) {
return ($lookup[$b] ?? -1) <=> ($lookup[$a] ?? -1);
});
var_export($aShips);
If you don't like reversing the lookup and sorting DESC, you can count the order array to determine a fallback value. This alternative script uses arrow function syntax for brevity and to gain direct access to the variables declared outside of the closure. (Demo)
$lookup = array_flip($order);
$default = count($order);
uksort($aShips, fn($a, $b) => ($lookup[$a] ?? $default) <=> ($lookup[$b] ?? $default));
var_export($aShips);

Sorting an array by values first, then keys

I want to sort this key-value array by values first, then keys.
This is the array:
$a = [
10 => 1,
4 => 2,
3 => 2
];
i want to get:
4 => 2,
3 => 2,
10 => 1
I tried to use arsort, but can't get current answer.
Use uksort to sort by keys, use those keys to look up the values in the array, do a comparison by values first and keys upon equality:
uksort($arr, function ($a, $b) use ($arr) {
if (($res = $arr[$a] - $arr[$b]) != 0) {
return $res;
}
return $a - $b;
});
See https://stackoverflow.com/a/17364128/476 for more in-depth explanations of sort callbacks.

array_filter based on keys from another array

I have two arrays:
$arr1 = array('a' => 10, 'b' => 20);
$arr2 = array('a' => 10, 'b' => 20, 'c' => 30);
How can I use array_filter to drop elements from $arr2 that don't exist in $arr1 ? Like "c" in my example...
There is a function specifically made for this purpose: array_intersect():
array_intersect — Computes the intersection of arrays
$arr2 = array_intersect($arr1, $arr2);
If you want to compare keys, not the values like array_intersect(), use array_intersect_key():
array_intersect_key — Computes the intersection of arrays using keys for comparison
$arr2 = array_intersect_key($arr1, $arr2);
If you want to compare key=>value pairs, use array_intersect_assoc():
array_intersect_assoc — Computes the intersection of arrays with additional index check
$arr2 = array_intersect_assoc($arr1, $arr2);
Use in_array in your array_filter callback:
$arr2 = array_filter($arr2, function($e) use ($arr1) {
return in_array($e, $arr1);
});
Note that this will regard the values of the elements, not the keys. array_filter will not give you any key to work with so if that is what you need a regular foreach loop may be better suited.
To get the elements that exist in $arr2 which also exist in $arr1 (i.e. drop elements of $arr2 that don't exist in $arr1), you can intersect based on the key like this:
array_intersect_key($arr2, $arr1); // [a] => 10, [b] => 20
Update
Since PHP 7 it's possible to pass mode to array_filter() to indicate what value should be passed in the provided callback function:
array_filter($arr2, function($key) use ($arr1) {
return isset($arr1[$key]);
}, ARRAY_FILTER_USE_KEY);
Since PHP 7.4 you can also drop the use () syntax by using arrow functions:
array_filter($arr2, fn($key) => isset($arr1[$key]), ARRAY_FILTER_USE_KEY);

Categories