Sorting multi-dimensional arrays by element size - php

I would like to be able to sort this array by the element array's size;
array(
[0] => array( [0] => 'B', [1] => 'C');
[1] => array( [0] => 'B');
[2] => array( [0] => 'A', [1] => 'C');
[3] => array( [0] => 'A', [1] => 'B', [2] => 'C');
[4] => array( [0] => 'C');
[5] => array( [0] => 'A');
[6] => array( [0] => 'A', [1] => 'B');
)
array(
[0] => array( [0] => 'A');
[1] => array( [0] => 'B');
[2] => array( [0] => 'C');
[3] => array( [0] => 'A', [1] => 'B');
[4] => array( [0] => 'A', [1] => 'C');
[5] => array( [0] => 'B', [1] => 'C');
[6] => array( [0] => 'A', [1] => 'B', [2] => 'C');
)

Using closures (PHP 5.3):
usort($array, function($a, $b) { return count($a) - count($b); });
Without using closures (PHP 5.2):
usort($array, create_function('$a, $b', 'return count($a) - count($b)'));
Note that $array will be sorted in place. Don't look for it in the return value of usort().

I have not tested this, but I hope you can get the point.
function yoursort($a,$b) {
if(!(is_array($a) && is_array($b)) return 0; // item of array is not an array
$cntA = sizeof($a);
$cntB = sizeof($b);
if($cntA!=$cntB) return $cntA-$cntB; // smaller array up
foreach($a as $key=>$val) {
$ordA = ord($a[$key]);
$ordB = ord($b[$key]);
if($ordA!=$ordB) return $ordA-$ordB; // sort sub array by alphabet
}
return 0; // items are equal
}
usort($yourarray,"yoursourt");
Here you can find more about usort
http://php.net/manual/en/function.usort.php

You can do this in 2 steps.
In step 1, sort individual arrays on
value.
In step 2, sort the 2D array, first
based on size and if the size are not
equal sort on the values.
Code:
foreach($input as $key => $val) {
sort($input[$key]);
}
usort($input, "cmp");
print_r($input);
function cmp($a,$b) {
if(count($a) != count($b)) {
return count($a) - count($b);
}
for($i=0;$i<count($a);$i++) {
if($a[$i] != $b[$i]) {
return strcmp($a[$i],$b[$i]);
}
}
return 0;
}
Code in Action

Related

PHP make some of the Key to be sticky in an Associative array

I try to use array_column and array_multisort to sort Array B by Key (ID).
However, I have a scenario whereby I need to have some ID to be sticky on the top .
For example, by comparing Array A and Array B, move ID 3 and ID 1 to the top of the Array B. The final result will be Array C .
Is there a PHP array function to achieve this? Please advice...
Array A
Array
(
[0] => Array
(
[ID] => 3
)
[1] => Array
(
[ID] => 1
)
)
1
Array B
Array
(
[0] => Array
(
[ID] => 1
[product] => A
)
[1] => Array
(
[ID] => 2
[product] => B
)
[2] => Array
(
[ID] => 3
[product] => C
)
[3] => Array
(
[ID] => 4
[product] => D
)
)
1
Array C (Result)
Array
(
[0] => Array
(
[ID] => 3
[product] => C
)
[1] => Array
(
[ID] => 1
[product] => A
)
[2] => Array
(
[ID] => 2
[product] => B
)
[3] => Array
(
[ID] => 4
[product] => D
)
)
1
The preparatory steps involved in generating the lookup and outlier values may be avoidable with more intimate knowledge of your project scope, but I'll only use the values that you have provided. (I mean, you could hardcode outlier as 9999999 and wrap array_flip around array_column for brevity.)
The max value simply needs to be a value higher than the highest ID in the priority array.
The lookup needs to have ID values as keys and their original indexes as the new values (hence the flip).
usort() is precisely the php function to use when performing a custom sorting process.
use is necessary to pass the required additional values into the custom function's scope.
The spaceship operator (<=>) is a fantastic way to package your two-condition sort logic. Each side will be compared using the values "left to right".
Code: (Demo)
$priority = [['ID' => 3], ['ID' => 1]];
$input = [['ID' => 1, 'product' => 'A'], ['ID' => 2, 'product' => 'B'], ['ID' => 3, 'product' => 'C'], ['ID' => 4, 'product' => 'D']];
$lookup = array_column($priority, 'ID');
$outlier = max($lookup) + 1;
$lookup = array_flip($lookup);
usort($input, function($a, $b) use ($lookup, $outlier) {
return [$lookup[$a['ID']] ?? $outlier, $a['ID']] <=> [$lookup[$b['ID']] ?? $outlier, $b['ID']];
});
var_export($input);
Output:
array (
0 =>
array (
'ID' => 3,
'product' => 'C',
),
1 =>
array (
'ID' => 1,
'product' => 'A',
),
2 =>
array (
'ID' => 2,
'product' => 'B',
),
3 =>
array (
'ID' => 4,
'product' => 'D',
),
)
Without the spaceship operator, the custom function gets noticeably more verbose.
Code: (Demo)
usort($input, function($a, $b) use ($lookup, $outlier) {
$bIsPriority = isset($lookup[$b['ID']]);
if (isset($lookup[$a['ID']])) {
if ($bIsPriority) {
return $lookup[$a['ID']] - $lookup[$b['ID']];
} else {
return -1;
}
} elseif ($bIsPriority) {
return 1;
} else {
return $a['ID'] - $b['ID'];
}
});
Here is one of doing it:
<?php
$a = [['ID' => 5], ['ID' => 1]];
$b = [['ID' => 1, 'product' => 'A'], ['ID' => 2, 'product' => 'B'], ['ID' => 3, 'product' => 'C'], ['ID' => 4, 'product' => 'D'], ['ID' => 5, 'product' => 'E']];
// $keysOnTop = Array([0] => 5, [1] => 1)
$keysOnTop = array_column($a, 'ID');
$temp1 = $temp2 = [];
foreach($b as $value){
if(in_array($value['ID'], $keysOnTop)){
$temp1[] = $value;
} else {
$temp2[] = $value;
}
}
// $temp1 = Array([0] => Array([ID] => 1, [product] => A), [1] => Array([ID] => 5, [product] => E))
// $temp2 = Array([0] => Array([ID] => 2, [product] => B), [1] => Array([ID] => 3, [product] => C), [2] => Array([ID] => 4, [product] => D))
$final_arr = array_merge($temp1, $temp2);
echo '<pre>';
print_r($final_arr);
// Output:
/*
Array
(
[0] => Array
(
[ID] => 1
[product] => A
)
[1] => Array
(
[ID] => 5
[product] => E
)
[2] => Array
(
[ID] => 2
[product] => B
)
[3] => Array
(
[ID] => 3
[product] => C
)
[4] => Array
(
[ID] => 4
[product] => D
)
)
*/
?>

Access multidimensional associative array using integer index

Assuming I have an associative array which looks like this:
$arr = array(
'a' => array(
'b' => 'b',
'c' => 'c',
'd' => array(
'e' =>'e'
),
'f' => 'f',
'g' => 'g'
),
'h' => 'h',
'i' => array(
'j' => 'j',
'k' => 'k'
)
);
Now I want to access array elements using integer-index:
0 will return array of key 'a'
1 will return array of key 'b'
2 will return array of key 'c'
3 will return array of key 'd'
4 will return array of key 'e'
..
11 will return array of key 'k'
I have tried to accomplish this by recursion using the following code:
function getElement($arr, $id)
{
static $counter = 0;
foreach($arr as $key => $val){
$varNum = count($val);
$total = $counter + $varNum;
if($counter == $id){
return $val;
}
elseif($total > $id){
$counter++;
$res = getElement($val, $id);
return $res;
}
$counter++;
}
}
However from index 4 the function fails.
any suggestions?
Using array_walk_recursive:
$list = [];
array_walk_recursive($arr, function($value) use (&$list) {
$list[] = $value;
});
print_r($list);
Will return something like:
Array
(
[0] => b
[1] => c
[2] => e
[3] => f
[4] => g
[5] => h
[6] => j
[7] => k
)
now to return a full list of keys , you may use the following function:
function walk_recursive($arr, &$list = [])
{
foreach ($arr as $k => $ar) {
if (is_array($ar)) {
$list[] = $k;
walk_recursive($ar, $list);
} else {
$list[] = $ar;
}
}
return $list;
}
print_r(walk_recursive($arr));
this output the following :
Array
(
[0] => a
[1] => b
[2] => c
[3] => d
[4] => e
[5] => f
[6] => g
[7] => h
[8] => i
[9] => j
[10] => k
)
live example: https://3v4l.org/Wv0nL

Array remapping - Can I do this better?

for starters I have the following array objects (id, foo and bar are a result of a database query and should be addressed as object variables ->id)
array([0] => array([id] => 1, [foo] => 'a'), [1] => array([id] => 2, [foo] => 'b')
and
array([0] => array([id] => 1, [bar] => 'b'), [1] => array([id] => 2, [bar] => 'a')
I want to create one new array with the id column as key
array([1] => array([foo] => 'a', [bar] => 'b'), [2] => array([foo] => 'b', [bar] => 'a')
I used the following lines of code to create the desired array:
foreach($array1 as $row1) {
$newArray1[$row1->id] = $row1;
}
foreach($array2 as $row2) {
$newArray2[$row2->id] = $row2;
}
foreach($array2 as $key => row3) { //array 2 is always longer or equal to array 1
$result[$key]['bar'] = $newArray2[$key]->bar;
if (isset($newArray1[$key])) {
$result[$key]['foo'] = $newArray1[$key]->foo;
} else {
$result[$key]['bar'] = 0;
}
}
I think this could be done a lot easier. Is this the case, if so, how?
If I understood you need to merge two arrays using as index the 'id' field, which is equal to both ones.
I would do this:
$newarray = array();
for($i=0;$i<count($array1);$i++)
$newarray[$array1[$i]["id"]] = array($array1[$i]["foo"], $array2[$i]["bar"]);
instead of creating new array you can do this:
$arr1=array('0' => array('id' => 1, 'foo' => 'a'), '1' => array('id' => 2, 'foo' => 'b'));
$arr2=array('0' => array('id' => 1, 'bar' => 'b'), '1' => array('id' => 2, 'bar' => 'a'));
for($i=0;$i<count($arr1);$i++){
$arr1[$i] = array("foo"=>$arr1[$i]["foo"], "bar"=>$arr2[$i]["bar"]);
}
print_r($arr1);

Sort Multi-dimensional array by decimal values

What I'm trying to do is sort a multi-dimensional array that contains decimal values. From what I've tested, floats are having trouble being ordered properly.
Array
(
[0] => Array
(
[company] => Ebay
[weight] => 4.6
)
[1] => Array
(
[company] => Ebay
[weight] => 1.7
)
[2] => Array
(
[company] => Ebay
[weight] => 3.7
)
)
usort($array, 'order_by_weight');
// Sorts DESC highest first
function order_by_weight($a, $b) {
return $b['weight'] - $a['weight'];
}
What is the best way to sort these numbers in descending?
$arr = array(
array('company' => 'A', 'weight' => 4.6),
array('company' => 'B', 'weight' => 1.7),
array('company' => 'C', 'weight' => 3.7),
);
usort($arr, 'order_by_weight');
function order_by_weight($a, $b) {
return $b['weight'] > $a['weight'] ? 1 : -1;
}
var_dump($arr);
PS: it's not a rocket science - this exact "trick" is used as the first example at http://php.net/usort
You can do this with anonymous function in just one line
$arr = array(
array('company' => 'A', 'weight' => 4.6),
array('company' => 'B', 'weight' => 1.7),
array('company' => 'C', 'weight' => 3.7),
);
usort($arr, function($a, $b) { return $b['weight'] > $a['weight'] ;});
print_r($arr);
Hope this helps :)
You can sort the array using array_multisort, altough, this is often used to sort on multiple array values instead of one.
echo "<pre>";
$a = array(
array('company' => 'ebay', 'weight' => 4.6),
array('company' => 'ebay', 'weight' => 1.7),
array('company' => 'ebay', 'weight' => 3.7),
array('company' => 'ebay', 'weight' => 2.7),
array('company' => 'ebay', 'weight' => 9.7),
array('company' => 'ebay', 'weight' => 0.7),
);
$company = array();
$weight = array();
foreach($a as $key=>$val) {
array_push($company, $val['company']);
array_push($weight, $val['weight']);
}
array_multisort($weight, SORT_ASC, $a);
print_r($a);
In case someone wants a more concise code, especially to handle equals condition you will add if condition before #zerkms's solution
using ceil will round fractions up and will sort the decimal numbers correctly
usort($data, function($a, $b)
{
return ceil($a[$_GET['sortby']] - $b[$_GET['sortby']]);
});
As sorting algo here is good example for sort multi dimension array without using any more inbuilt php function
$multiarr = array('0'=>array(
"hashtag" => "a7e87329b5eab8578f4f1098a152d6f4",
"title" => "Flower",
"order" => 3),
'1' => array(
'hashtag' => "b24ce0cd392a5b0b8dedc66c25213594",
"title" => "Free",
"order" => 2),
'2' => array('hashtag' => 'e7d31fc0602fb2ede144d18cdffd816b',
'title' => 'Ready',
'order' => 1
));
sorting function :
function multisort(&$array, $key) {
$valsort = array();
$ret = array();
reset($array);
foreach ($array as $ii => $va) {
$valsort[$ii] = $va[$key];
}
asort($valsort);
foreach ($valsort as $ii => $va) {
$ret[$ii] = $array[$ii];
}
$array = $ret;
}
multisort($multiarr, "order");
: output :
Array
(
[2] => Array
(
[hashtag] => e7d31fc0602fb2ede144d18cdffd816b
[title] => Ready
[order] => 1
)
[1] => Array
(
[hashtag] => b24ce0cd392a5b0b8dedc66c25213594
[title] => Free
[order] => 2
)
[0] => Array
(
[hashtag] => a7e87329b5eab8578f4f1098a152d6f4
[title] => Flower
[order] => 3
)
)
None of the earlier posted answers are demonstrating the most modern syntaxes for sorting by a column value.
If using usort(), use the three-way comparison operator ("spaceship operator ") from PHP7 and if you are on PHP7.4 or higher enjoy the brevity of "arrow function" syntax. For descending directional sorting write $b on the left of the operator and $a on the right. (Demo)
usort($arr, fn($a, $b) => $b['weight'] <=> $a['weight']);
array_multisort() can also be used, but it requires an additional loop to isolate a column of data. (Demo)
array_multisort(array_column($arr, 'weight'), SORT_DESC, $arr);

PHP multi sort array

If I have this array:
array(
[0] => array(5, "hi"),
[1] => array(2, "hello"),
[2] => array(9, "test")
)
How can I sort this array by [n][0]?
The end result should to be like this:
array(
[0] => array(9, "test")
[1] => array(5, "hi"),
[2] => array(2, "hello"),
)
You can use a custom comparison function:
function multi_sort($a, $b)
{
return $a[0] < $b[0];
}
usort($array, 'multi_sort');

Categories