Sorting a table in PHP - php

I have some difficulty sorting my array. It looks like this :
[0] => Array
(
[firstname] => Jnic
[lastname] => Fortin
[points] => Array
(
[id] => 20453
[f] => 31
[r] => 7
[total] => 82
)
)
[1] => Array
(
[firstname] => Kris
[lastname] => Anders
[points] => Array
(
[id] => 20309
[f] => 0
[r] => 1
[total] => 56
)
)
[2] => Array
(
[firstname] => Em
[lastname] => Zajo
[points] => Array
(
[id] => 20339
[f] => 8
[r] => 3
[total] => 254
)
)
I would like to sort it by "total" DESC. How could I do it? If everything sort ok the array would be order [2][0][1] (254,82,56)

You probably can use the usort function for that : it sorts an array, using a callback function to compare the elements of that array :
bool usort ( array &$array , callback $cmp_function )
This function will sort an array by
its values using a user-supplied
comparison function. If the array you
wish to sort needs to be sorted by
some non-trivial criteria, you should
use this function
If your function is defined to compare per $element['points']['total'], it should do the trick.
Edit : And here is the example, using uasort, which is the same as usort, but will keep the array keys, like pointed out by ryanday :
First, let's declare the array :
$a = array(
array(
'firstname' => 'Jnic',
'lastname' => 'Fortin',
'points' => array(
'id' => 20453,
'f' => 31,
'r' => 7,
'total' => 82,
),
),
array(
'firstname' => 'Kris',
'lastname' => 'Anders',
'points' => array(
'id' => 20309,
'f' => 0,
'r' => 1,
'total' => 56,
),
),
array(
'firstname' => 'Em',
'lastname' => 'Zajo',
'points' => array(
'id' => 20339,
'f' => 8,
'r' => 3,
'total' => 254,
),
),
);
And then, the comparison function :
function my_compare($a, $b) {
if ($a['points']['total'] > $b['points']['total']) {
return -1;
} else if ($a['points']['total'] < $b['points']['total']) {
return 1;
}
return 0;
}
And, finally, we use it :
uasort($a, 'my_compare');
var_dump($a);
And get the array, sorted by total desc :
array
2 =>
array
'firstname' => string 'Em' (length=2)
'lastname' => string 'Zajo' (length=4)
'points' =>
array
'id' => int 20339
'f' => int 8
'r' => int 3
'total' => int 254
0 =>
array
'firstname' => string 'Jnic' (length=4)
'lastname' => string 'Fortin' (length=6)
'points' =>
array
'id' => int 20453
'f' => int 31
'r' => int 7
'total' => int 82
1 =>
array
'firstname' => string 'Kris' (length=4)
'lastname' => string 'Anders' (length=6)
'points' =>
array
'id' => int 20309
'f' => int 0
'r' => int 1
'total' => int 56
ryanday > Thanks for your answer !

You will want to use usort as answered by #Pascal MARTIN, but here is the full code to achieve what you want:
function total_sort($a, $b){
$a_total = $a['points']['total'];
$b_total = $b['points']['total'];
if($a_total == $b_total) return 0;
return ($a_total > $b_total) ? -1 : 1;
}
usort($array, "total_sort");
EDIT: After I posted I saw #Pascal updated his answer to include a sample. Since I wrote my sort function a little different, I am leaving it here as another reference.

usort is a robust solution that can be very flexible for complex cases. Since your data set is relatively simple i would suggest the following:
// assuming your array has been defined in $a
$sort = array();
foreach ($a as $key => $suba)
{
// this collects the values you want to sort by and associates them with the correct index
$sort[$key] = $suba['points']['total'];
}
// this sorts the collected values
sort($sort);
// this re-sorts $a according to the sorted $sort array
array_multisort($a, $sort);
not sure about performance but this is at least AS GOOD as usort if not better

I noticed you said the proper array should be ordered [2][0][1], if that index association is important to you follow Pascal's advice with the uasort() function.

Related

How can match two array in php

I have two array:
One Array
$workingDays = ['2019-11-01','2019-11-02','2019-11-03','2019-11-04'];
Other Array
$doneWork = array(
array(
'id' => 1,
'date' => '2019-11-01',
'work' => 'done'
),
array(
'id' => 1,
'date' => '2019-11-02',
'work' => 'done'
),
array(
'id' => 1,
'date' => '2019-11-04',
'work' => 'done'
)
);
My Question: How can check which date not exist in $doneWork array
You'd start by extracting the dates from $doneWork using array_map.
$doneWork = [
[
'id' => 1,
'date' => '2019-11-01',
'work' => 'done',
],
[
'id' => 1,
'date' => '2019-11-02',
'work' => 'done',
],
[
'id' => 1,
'date' => '2019-11-04',
'work' => 'done',
],
];
$doneWorkDays = array_map(function ($element) {
return $element['date'];
}, $doneWork);
print_r($doneWorkDays);
Will print:
Array ( [0] => 2019-11-01 [1] => 2019-11-02 [2] => 2019-11-04 )
Then check which elements in $workingDays are not in such array, using array_diff
$diff = array_diff($workingDays, $doneWorkDays);
print_r($diff);
Will print:
Array ( [2] => 2019-11-03 )
Pay attention, the result gives you not only the elements but also their index in the original array. If you don't care about these, use instead:
print_r(array_values($diff));
Why would anyone need such index? Well, perhaps you could need to report not only how many days were missed but also check if two missing elements are adjacent.
(the indexes are relevant only for the array you're comparing against. It doesn't matter in what position they appear in $doneWork )
Edit:
You say you need the results to be in "doneWork" format, which is an associative array with id (always zero), date and work (always 'absent').
Let's say your workin days are now
$workingDays = [
'2019-11-01',
'2019-11-02',
'2019-11-03',
'2019-11-04',
'2019-11-05'
];
So there are two missing days. Again, array map to the rescue:
// from previous answer
$diff = (array_diff($workingDays, $doneWorkDays));
// map each missing date to a doneWork array
$diff_assoc = array_values(
array_map(function($date) {
return [
'id' => 0,
'date' => $date,
'work' => 'absent'
];
},$diff)
);
That will return
Array
(
[0] => Array
(
[id] => 0
[date] => 2019-11-03
[work] => absent
)
[1] => Array
(
[id] => 0
[date] => 2019-11-05
[work] => absent
)
)
Again, note I'm wrapping the result in array_values because you need a plain array as result instead of:
Array
(
[2] => Array
(
[id] => 0
[date] => 2019-11-03
[work] => absent
)
[4] => Array
(
[id] => 0
[date] => 2019-11-05
[work] => absent
)
)
$nonExistingDates = [];
$doneWorkDays = [];
foreach($doneWork as $work) {
$doneWorkDays[] = $work['date'];
}
$nonExistingDates = array_diff($workingDays, $doneWorkDays);
// optional, removes duplicate dates
$nonExistingDates = array_unique($nonExistingDates);

php array merge on key using multidimensional array [duplicate]

This question already has answers here:
Merge row data from multiple arrays
(6 answers)
Closed 4 months ago.
This is my array:
array
0 =>
array
'id' => int 220950
'order_reference' => string '600125479'
1 =>
array
'id' => int 220985
'order_reference' => string '498638'
and this my another array
array
0 =>
array
'entity_id' => 1
'order_status' => 'test'
1 =>
array
'entity_id' => 2
'order_status' => 'test2'
and my goal is to achieve this:
array
0 =>
array
'id' => int 220950
'order_reference' => string '600125479'
'entity_id' => 1
'order_status' => 'test'
1 =>
array
'id' => int 220985
'order_reference' => string '498638'
'entity_id' => 2
'order_status' => 'test2'
with array_merge I managed to get this(NOT my desired goal) and this all I found on stackoverflow and other forums:
array
0 =>
array
'id' => int 220950
'order_reference' => string '600125479'
1 =>
array
'id' => int 220985
'order_reference' => string '498638'
array
2 =>
array
'entity_id' => 1
'order_status' => 'test'
3 =>
array
'entity_id' => 2
'order_status' => 'test2'
Any ideas or suggestions are welcomed :) thank you
Using a foreach I can add the keys values, but I am looking for a more cleanest way :)
Here's the answer to your question using array_map and array_merge_recursive.
<?php
$array1 = array(
[
"id" => 220950,
"order_reference" => "600125479"
],
[
"id" => 220985,
"order_reference" => "498638"
]
);
$array2 = array(
[
"entity_id" => 1,
"order_status" => "test"
],
[
"entity_id" => 2,
"order_status" => "test"
]
);
$results = array();
array_map(function($array1, $array2) use (&$results) {
$results[] = array_merge_recursive($array1, $array2);
}, $array1, $array2);
var_dump($results);
This will output:
array (size=2)
0 =>
array (size=4)
'id' => int 220950
'order_reference' => string '600125479' (length=9)
'entity_id' => int 1
'order_status' => string 'test' (length=4)
1 =>
array (size=4)
'id' => int 220985
'order_reference' => string '498638' (length=6)
'entity_id' => int 2
'order_status' => string 'test' (length=4)
With the caveat that your arrays don't have a joint value to compare against so you need to make sure that the keys always line up, I think a loop like this will do the trick:
for ($i = 0; $i < count($array1); $i++)
{
$new_array[] = array_merge($array1[$i], $array2[$i]);
}
Edit: You can also use array_map() but it doesn't offer any performance advantages AFAIK, and is overall less readable.
$new_array = array_map(function($a1_v, $a2_v) { return array_merge($a1_v, $a2_v); }, $a1, $a2);
Use array_merge with array_map
$array1 = array(
[
"id" => 220950,
"order_reference" => "600125479"
],
[
"id" => 220985,
"order_reference" => "498638"
]
);
$array2 = array(
[
"entity_id" => 1,
"order_status" => "test"
],
[
"entity_id" => 2,
"order_status" => "test2"
]
);
$result = array_map("array_merge",$array1,$array2);
print_r($result);
Output
Array
(
[0] => Array
(
[id] => 220950
[order_reference] => 600125479
[entity_id] => 1
[order_status] => test
)
[1] => Array
(
[id] => 220985
[order_reference] => 498638
[entity_id] => 2
[order_status] => test2
)
)
Working example

sort associative array by key value

I try to sort my array by "delais_livraison" by DESC in php but i dont know how i can do that
In the first level I expected to have in first Etoile => 25 and then Feuillage => 10
Array
(
[A] => Array
(
[0] => Array
(
[Feuillage] => Array
(
[delais_livraison] => 10
)
)
[1] => Array
(
[Étoiles] => Array
(
[delais_livraison] => 25
)
)
)
[B] => Array
(
[0] => Array
(
[Grenouillère] => Array
(
[delais_livraison] => 7
)
)
[1] => Array
(
[Chaussons] => Array
(
[delais_livraison] => 0
)
)
)
)
Edit :
this how my array has been build
pastebin
Just try with:
$data = array(
'A' => array(
array('Feuillage' => array('delais_livraison' => 10)),
array('Étoiles' => array('delais_livraison' => 25)),
),
'B' => array(
array('Grenouillère' => array('delais_livraison' => 7)),
array('Chaussons' => array('delais_livraison' => 0)),
),
);
foreach ($data as &$group) {
usort($group, function($itemA, $itemB){
$a = current($itemA)['delais_livraison'];
$b = current($itemB)['delais_livraison'];
if ($a == $b) return 0;
return ($a > $b) ? -1 : 1;
});
}
Output:
array (size=2)
'A' =>
array (size=2)
0 =>
array (size=1)
'Étoiles' =>
array (size=1)
'delais_livraison' => int 25
1 =>
array (size=1)
'Feuillage' =>
array (size=1)
'delais_livraison' => int 10
'B' => &
array (size=2)
0 =>
array (size=1)
'Grenouillère' =>
array (size=1)
'delais_livraison' => int 7
1 =>
array (size=1)
'Chaussons' =>
array (size=1)
'delais_livraison' => int 0
Solution uses anonymous function that has been introduced in PHP 5.3.0. For older versions just create a function and specify it in usort function:
function my_sort_function($itemA, $itemB){
// ...
}
usort($group, 'my_sort_function');
Since your array has two levels, how do you want to manage a global sort ?
Do you want to do a sort for each sub array ( A, B, ...) ?
In this case the approach will be to use function uksort for each sub array (A and B) and define your own compare function to compare delais_livraison values.
You would appear to be looking for ksort, or, if you have to supply your own comparison function, uksort.

How can I group by a count of a subvalue of an array?

I've been trying every combination of array_count_values and array_map I can think of. I'm hoping you can show me the way.
Here's a sample array:
$arr = array(
array('name' => 'foo','score' => 1),
array('name' => 'foo','score' => 2),
array('name' => 'bar','score' => 1)
);
I'd like to combine the scores of all sub values with the same name. Something like GROUP BY name in MySQL.
Array
(
[0] => Array
(
[name] => foo
[score] => 3
)
[1] => Array
(
[name] => bar
[score] => 1
)
)
I know there are other questions that sort multidimensional arrays, but I'm not able to find one that sums one of the sub values along the way.
You can use array_reduce
$arr = array(
array('name' => 'foo','score' => 1),
array('name' => 'foo','score' => 2),
array('name' => 'bar','score' => 1));
$array = array_reduce($arr, function ($a, $b) {
isset($a[$b['name']]['score']) ? $a[$b['name']]['score'] += $b['score'] : $a[$b['name']] = $b;
return $a;
});
var_dump($array);
Output
array
'foo' =>
array
'name' => string 'foo' (length=3)
'score' => int 3
'bar' =>
array
'name' => string 'bar' (length=3)
'score' => int 1

How can I preserve multidimensional array information when performing array intersections in PHP?

I have many arrays containing an ID as the primary key, with multidimensional information under each id. Here are two examples:
First Example Array:
array
14181 =>
array
'industries' =>
array
'weight' => string '105652' (length=6)
'count' => string '11' (length=2)
48354 =>
array
'industries' =>
array
'weight' => string '508866' (length=6)
'count' => string '10' (length=2)
Second Example Array:
array
16434 =>
array
'business_types' =>
array
'weight' => string '104614' (length=6)
'count' => string '1' (length=1)
48354 =>
array
'business_types' =>
array
'weight' => string '103610' (length=6)
'count' => string '10' (length=2)
I'd like to get the intersection of many arrays like these ( based on the key ), but I need to preserve the weight and count data from each array for each key. Notice it's different weight and count data from each array. In this case, business_type and industries.
Final Array Needed:
array
48354 =>
array
'business_types' =>
array
'weight' => string '103610' (length=6)
'count' => string '10' (length=2)
'industries' =>
array
'weight' => string '508866' (length=6)
'count' => string '10' (length=2)
Originally I was not trying to keep the weights and counts, so I was simply performing an array_intersect_keys() and the job was done. Now I need to keep this data. I named the sub arrays different things in the hope that array_intersect_keys() would preserve it, however, it only preserves it for the first array in the function.
Is there a preferred way to do something like this?
The only solution I can come up with is to reduce all the arrays to a list of final ID's ( keys ) and then loop through that array pulling the weight and count info from each of the original arrays we compared.
Your proposed solution seems fine, but you might consider merging one list into the other, e.g.
<?php
$a1 = array(14181 => array('industries' => array('weight' => "105652", 'count' => "11")),
48354 => array('industries' => array('weight' => "508866", 'count' => "10")));
$a2 = array(16434 => array('business_types' => array('weight' => "104614", 'count' => "1")),
48354 => array('business_types' => array('weight' => "103610", 'count' => "10")));
//print_r($a1);
//print_r($a2);
foreach($a2 as $a2k => $a2v)
{
// The loop below should only go through once, but if there are multiple types it will be ok
foreach($a2v as $a2vk => $a2vv)
{
$a1[$a2k][$a2vk] = $a2vv;
}
}
printf("Output:\n");
print_r($a1);
printf("Output:\n");
print_r($a1);
Output:
Output:
Array
(
[14181] => Array
(
[industries] => Array
(
[weight] => 105652
[count] => 11
)
)
[48354] => Array
(
[industries] => Array
(
[weight] => 508866
[count] => 10
)
[business_types] => Array
(
[weight] => 103610
[count] => 10
)
)
[16434] => Array
(
[business_types] => Array
(
[weight] => 104614
[count] => 1
)
)
)
I believe that this will be slightly faster than your proposed solution.
Edit: The above was an array merge type algorithm that would merge the arrays even if a key wasn't common among the arrays. The algorithm below will only produce the intersection of the two arrays. My fault in initially getting the question:
<?php
$a1 = array(14181 => array('industries' => array('weight' => "105652", 'count' => "11")),
48354 => array('industries' => array('weight' => "508866", 'count' => "10")));
$a2 = array(16434 => array('business_types' => array('weight' => "104614", 'count' => "1")),
48354 => array('business_types' => array('weight' => "103610", 'count' => "10")));
//print_r($a1);
//print_r($a2);
// Pass the smaller array as first argument
if(count($a1) <= count($a2))
{
$res = my_merge($a1, $a2);
}
else
{
$res = my_merge($a2, $a1);
}
function my_merge($a1, $a2)
{
$res = array();
foreach($a1 as $a1k => $a1v)
{
if(array_key_exists($a1k, $a2))
{
$res[$a1k] = array_merge($a1[$a1k], $a2[$a1k]);
}
}
return $res;
}
printf("Output:\n");
print_r($res);
Output:
Array
(
[48354] => Array
(
[industries] => Array
(
[weight] => 508866
[count] => 10
)
[business_types] => Array
(
[weight] => 103610
[count] => 10
)
)
)

Categories