Reputation: 523
I've got an array that looks like this:
[
{
"id": "1",
"country_id": "1",
"spec_id": "1",
"spec_children_name": "SUPER REDUCES RATE",
"spec_children_first_col": "",
"spec_children_second_col": "",
"spec_children_third_col": ""
},
{
"id": "2",
"country_id": "1",
"spec_id": "1",
"spec_children_name": "REDUCED RATE",
"spec_children_first_col": "10% and 13%",
"spec_children_second_col": "food, passenger transport, accommodotion, newspaper, pharmaceutical products,\u2026.(10%); plants, antiques, firewood, cinema, theatre,\u2026(13%)",
"spec_children_third_col": ""
},
{
"id": "3",
"country_id": "1",
"spec_id": "1",
"spec_children_name": "MEDIUM RATE",
"spec_children_first_col": "",
"spec_children_second_col": "",
"spec_children_third_col": ""
},
{
"id": "4",
"country_id": "1",
"spec_id": "1",
"spec_children_name": "STANDARD RATE",
"spec_children_first_col": "20%",
"spec_children_second_col": "other",
"spec_children_third_col": ""
},
{
"id": "5",
"country_id": "1",
"spec_id": "1",
"spec_children_name": "ZERO RATE",
"spec_children_first_col": "",
"spec_children_second_col": "",
"spec_children_third_col": ""
},
{
"id": "104",
"country_id": "2",
"spec_id": "1",
"spec_children_name": "REDUCED RATE",
"spec_children_first_col": "TEXT 547",
"spec_children_second_col": "TEXT 1000",
"spec_children_third_col": ""
}
]
What i want: I'd like to sort this array by 2 object key compare, if spec_children_name
and spec_id
. Finally, it should look like:
[
[
{
"id": "1",
"country_id": "1",
"spec_id": "1",
"spec_children_name": "SUPER REDUCES RATE",
"spec_children_first_col": "",
"spec_children_second_col": "",
"spec_children_third_col": ""
},
{
"id": "2",
"country_id": "1",
"spec_id": "1",
"spec_children_name": "REDUCED RATE",
"spec_children_first_col": "10% and 13%",
"spec_children_second_col": "food, passenger transport, accommodotion, newspaper, pharmaceutical products,\u2026.(10%); plants, antiques, firewood, cinema, theatre,\u2026(13%)",
"spec_children_third_col": ""
},
,
{
"id": "104",
"country_id": "2",
"spec_id": "1",
"spec_children_name": "REDUCED RATE",
"spec_children_first_col": "TEXT 547",
"spec_children_second_col": "TEXT 1000",
"spec_children_third_col": ""
}
{
"id": "3",
"country_id": "1",
"spec_id": "1",
"spec_children_name": "MEDIUM RATE",
"spec_children_first_col": "",
"spec_children_second_col": "",
"spec_children_third_col": ""
},
{
"id": "4",
"country_id": "1",
"spec_id": "1",
"spec_children_name": "STANDARD RATE",
"spec_children_first_col": "20%",
"spec_children_second_col": "other",
"spec_children_third_col": ""
},
{
"id": "5",
"country_id": "1",
"spec_id": "1",
"spec_children_name": "ZERO RATE",
"spec_children_first_col": "",
"spec_children_second_col": "",
"spec_children_third_col": ""
}
]
*Note the two objects with same keys (spec_id
and spec_children_name
) one after another.
What i've tried so far:
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;
}
Fn call: array_sort($array, 'spec_children_name', SORT_ASC);
Dynamic solution please, with key parameters to sort by
Another approach:
usort($country_specs_meta_results, function($a, $b){
$c .= $b['spec_id'] - $a['spec_id'];
$c .= strcmp($a['spec_children_name'], $b['spec_children_name']);
return $c;
});
EDIT: I've updated the arrays as solution above still wreck the output
EDIT 2: This is the output with the function array_sort modified by @Jeto:
{
"4": {
"id": "5",
"country_id": "1",
"spec_id": "1",
"spec_children_name": "ZERO RATE",
"spec_children_first_col": "",
"spec_children_second_col": "",
"spec_children_third_col": ""
},
"0": {
"id": "1",
"country_id": "1",
"spec_id": "1",
"spec_children_name": "SUPER REDUCES RATE",
"spec_children_first_col": "",
"spec_children_second_col": "",
"spec_children_third_col": ""
},
"3": {
"id": "4",
"country_id": "1",
"spec_id": "1",
"spec_children_name": "STANDARD RATE",
"spec_children_first_col": "20%",
"spec_children_second_col": "other",
"spec_children_third_col": ""
},
"1": {
"id": "2",
"country_id": "1",
"spec_id": "1",
"spec_children_name": "REDUCED RATE",
"spec_children_first_col": "10% and 13%",
"spec_children_second_col": "food, passenger transport, accommodotion, newspaper, pharmaceutical products,\u2026.(10%); plants, antiques, firewood, cinema, theatre,\u2026(13%)",
"spec_children_third_col": ""
},
"76": {
"id": "104",
"country_id": "2",
"spec_id": "1",
"spec_children_name": "REDUCED RATE",
"spec_children_first_col": "10% and 13% BG",
"spec_children_second_col": "TEXT FOR BG",
"spec_children_third_col": ""
},
"2": {
"id": "3",
"country_id": "1",
"spec_id": "1",
"spec_children_name": "MEDIUM RATE",
"spec_children_first_col": "",
"spec_children_second_col": "",
"spec_children_third_col": ""
}
}
So the order changed but not for the "spec_children_name": "REDUCED RATE"
only
Upvotes: 0
Views: 84
Reputation: 3433
Your question is essentially how to sort an array according to two keys. In many programming languages, PHP included, the answer would be to create a comparison function (sometimes dubbed a comparator), that returns values (commonly -1, 0, 1) depending on the required relative ordering of its arguments. If the two arguments differ on the first of the two keys, return -1 or 1 depending on the first key's ordering. The they are equal on the first key, check the second key - and return -1, 0, 1 according to the ordering of the second key. This logic can naturally be extended to an arbitrary amount of keys. The PHP Spaceship operator <=>
implements this logic for many built-in types.
Below is code that demonstrates this for your scenario using PHP. It is adapted from this question. Here, I assumed that you wanted to sort by the rate (zero < super reduced < reduced < medium < standard) and then the spec_id
(numerically). I also corrected SUPER REDUCES RATE
to SUPER REDUCED RATE
.
$RATE_ORDERING = array(
'ZERO RATE' => 1,
'SUPER REDUCED RATE' => 2,
'REDUCED RATE' => 3,
'MEDIUM RATE' => 4,
'STANDARD RATE' => 5
);
$arr = array(
array('spec_id' => 2, 'spec_children_name' => 'STANDARD RATE'),
array('spec_id' => 1, 'spec_children_name' => 'STANDARD RATE'),
array('spec_id' => 2, 'spec_children_name' => 'ZERO RATE'),
array('spec_id' => 1, 'spec_children_name' => 'MEDIUM RATE'),
array('spec_id' => 2, 'spec_children_name' => 'REDUCED RATE'),
array('spec_id' => 2, 'spec_children_name' => 'MEDIUM RATE'),
array('spec_id' => 1, 'spec_children_name' => 'SUPER REDUCED RATE'),
array('spec_id' => 2, 'spec_children_name' => 'SUPER REDUCED RATE'),
array('spec_id' => 1, 'spec_children_name' => 'REDUCED RATE'),
array('spec_id' => 1, 'spec_children_name' => 'ZERO RATE')
);
usort($arr, function ($a, $b) use ($RATE_ORDERING) {
$result = $RATE_ORDERING[$a['spec_children_name']] - $RATE_ORDERING[$b['spec_children_name']];
if ($result != 0) {
return $result;
}
return $a['spec_id'] - $b['spec_id'];
});
var_dump($arr);
Output:
array(10) {
[0]=>
array(2) {
["spec_id"]=>
int(1)
["spec_children_name"]=>
string(9) "ZERO RATE"
}
[1]=>
array(2) {
["spec_id"]=>
int(2)
["spec_children_name"]=>
string(9) "ZERO RATE"
}
[2]=>
array(2) {
["spec_id"]=>
int(1)
["spec_children_name"]=>
string(18) "SUPER REDUCED RATE"
}
[3]=>
array(2) {
["spec_id"]=>
int(2)
["spec_children_name"]=> [17/133]
string(18) "SUPER REDUCED RATE"
}
[4]=>
array(2) {
["spec_id"]=>
int(1)
["spec_children_name"]=>
string(12) "REDUCED RATE"
}
[5]=>
array(2) {
["spec_id"]=>
int(2)
["spec_children_name"]=>
string(12) "REDUCED RATE"
}
[6]=>
array(2) {
["spec_id"]=>
int(1)
["spec_children_name"]=>
string(11) "MEDIUM RATE"
}
[7]=>
array(2) {
["spec_id"]=>
int(2)
["spec_children_name"]=>
string(11) "MEDIUM RATE"
}
[8]=>
array(2) {
["spec_id"]=>
int(1)
["spec_children_name"]=>
string(13) "STANDARD RATE"
}
[9]=>
array(2) {
["spec_id"]=>
int(2)
["spec_children_name"]=>
string(13) "STANDARD RATE"
}
}
Upvotes: 1
Reputation: 47864
The spaceship operator will take care of all data types automatically. Pass the dynamic column name and the sorting direction "factor" via use()
into the custom function scope.
Code: (Demo)
$objects = json_decode($json);
$column = 'spec_children_name';
$direction = 'asc';
$reverser = $direction === 'asc' ? 1 : -1;
uasort($objects, function($a, $b) use ($column, $reverser) {
return $reverser * ($a->$column <=> $b->$column);
});
var_export($objects);
Or perhaps... (Demo)
$objects = json_decode($json);
$rules = ['spec_id' => 'ASC', 'spec_children_name' => 'DESC'];
uasort($objects, function($a, $b) use ($rules) {
foreach ($rules as $column => $order) {
$left[] = $order === 'ASC' ? $a->$column : $b->$column;
$right[] = $order === 'ASC' ? $b->$column : $a->$column;
}
return $left <=> $right;
});
var_export($objects);
Upvotes: 0
Reputation: 7485
There's hardly a reason to reinvent a function as array_multisort and array_column will do.
$data =
[
[
'name' => 'John',
'age' => 34
],
[
'name' => 'Jack',
'age' => 55,
],
[
'name' => 'Adam',
'age' => 42
],
[
'name' => 'Jack',
'age' => 78
],
[
'name' => 'Adam',
'age' => 80
]
];
array_multisort(array_column($data, 'name'), SORT_ASC, array_column($data, 'age'), SORT_DESC, $data);
var_export($data);
Output:
array (
0 =>
array (
'name' => 'Adam',
'age' => 80,
),
1 =>
array (
'name' => 'Adam',
'age' => 42,
),
2 =>
array (
'name' => 'Jack',
'age' => 78,
),
3 =>
array (
'name' => 'Jack',
'age' => 55,
),
4 =>
array (
'name' => 'John',
'age' => 34,
),
)
Upvotes: 0