Reputation: 352
I want to order my associative rows by their column value as sequences of ascending values.
Sample array1:
$args = [
'a' => ['zebra' => 1],
'b' => ['zebra' => 0],
'c' => ['zebra' => 0],
'd' => ['zebra' => 0],
'e' => ['zebra' => 1],
];
Desired result:
[
'b' => ['zebra' => 0],
'a' => ['zebra' => 1],
'c' => ['zebra' => 0],
'e' => ['zebra' => 1],
'd' => ['zebra' => 0],
]
Notice that duplicate values are not consecutive while sorting ascending. Instead, all unique, first-encountered values come first, then second encountered values, etc.
Sample array2:
$args = [
'a' => ['zebra' => 1],
'b' => ['zebra' => 1],
'c' => ['zebra' => 1],
'd' => ['zebra' => 1],
'e' => ['zebra' => 0],
'f' => ['zebra' => 0],
];
Desired result:
[
'e' => ['zebra' => 0],
'a' => ['zebra' => 1],
'f' => ['zebra' => 0],
'b' => ['zebra' => 1],
'c' => ['zebra' => 1],
'd' => ['zebra' => 1],
]
Edit: I tried to do this with usort, via this similar, but different, question, and the answer was no, so I am looking for a programatic solution (without usort).
Upvotes: -2
Views: 165
Reputation: 14927
Here's a solution using array_map
after grabbing 1s and 0s in separate arrays:
$args0 = array_filter($args, function ($arg) {
return $arg['zebra'] === 0;
});
$args1 = array_filter($args, function ($arg) {
return $arg['zebra'] === 1;
});
$result = array_merge(...array_map(static function ($arg0Key, $arg1Key) use ($args0, $args1) {
if ($arg0Key !== null) {
$result[$arg0Key] = $args0[$arg0Key];
}
if ($arg1Key !== null) {
$result[$arg1Key] = $args1[$arg1Key];
}
return $result;
}, array_keys($args0), array_keys($args1)));
print_r($result);
Demo: https://3v4l.org/sfqeq
Note: using two array_filter
to separate values looks nice but loops over $args
twice; prefer a simple loop if the initial array can be somewhat big. This is not the relevant part of the answer, though.
Upvotes: 0
Reputation: 57131
Same idea to split the input into the 1's and 0's, then output a 0 and a 1 as long as there is something left to output. As each time you output a value, the array is reduced, this just continues till both lists are empty so should cope with unbalanced lists...
$temp = [ 0 => [], 1 => []];
foreach($args as $key=>$value){
$temp[$value['zebra']][] = $key;
}
$output = [];
while ( !empty($temp[0]) || !empty($temp[1]) ) {
if ( !empty($temp[0]) ) {
$next = array_shift($temp[0]);
$output [$next] = $args[$next];
}
if ( !empty($temp[1]) ) {
$next = array_shift($temp[1]);
$output [$next] = $args[$next];
}
}
Upvotes: 0
Reputation: 4599
I can suggest you to use deconstruction with count of comparison.
At first step you can collect all indexes with zebra = 1
and with zebra = 0
:
$zeros = [];
$ones = [];
foreach($args as $let=>$arg){
if ($arg['zebra'] === 1) {
$ones[] = $let;
} else if ($arg['zebra'] === 0) {
$zeros[] = $let;
}
}
And now you can construct resultant array like:
if(abs(count($zeros) - count($ones)) === 1) { // if their difference equal to 1
if (count($ones) > count($zeros)){ // if $ones is bigger
foreach($zeros as $ind=>$let){
$res[$ones[$ind]] = ['zebra' => 1];
$res[$let] = ['zebra' => 0];
$tmp = $ind;
}
$res[$ones[$tmp+1]] = ['zebra' => 1];
} else if (count($ones) < count($zeros)){ // if $zeros is bigger
foreach($ones as $ind=>$let){
$res[$zeros[$ind]] = ['zebra' => 0];
$res[$let] = ['zebra' => 1];
$tmp = $ind;
}
$res[$zeros[$tmp+1]] = ['zebra' => 0];
}
}
Output:
Array
(
[b] => Array
(
[zebra] => 0
)
[a] => Array
(
[zebra] => 1
)
[c] => Array
(
[zebra] => 0
)
[e] => Array
(
[zebra] => 1
)
[d] => Array
(
[zebra] => 0
)
)
If you need result in case of (1,0,1,0,0) use next constructor:
if (count($ones) > count($zeros)){
foreach($ones as $ind=>$let){
if (isset($zeros[$ind])) $res[$zeros[$ind]] = ['zebra' => 0];
$res[$let] = ['zebra' => 1];
}
} else if (count($zeros) > count($ones)){
foreach($zeros as $ind=>$let){
$res[$let] = ['zebra' => 0];
if (isset($ones[$ind])) $res[$ones[$ind]] = ['zebra' => 1];
}
}
Output:
Array
(
[b] => Array
(
[zebra] => 0
)
[a] => Array
(
[zebra] => 1
)
[d] => Array
(
[zebra] => 0
)
[c] => Array
(
[zebra] => 1
)
[e] => Array
(
[zebra] => 0
)
)
Upvotes: 0