Can't seem to get my head around of sorting laravel collection so empty / null data would end up being last. ( bit confused about usort )
Pretty much all I have is bunch of times / timestamps that need to be ordered. Some rows may not have for that column.
I would like data to appear ASC / ascending while empty/null data is shown last.
$collection->sortBy('timestamp') sorts nicely but doesn't know how to deal with empty fields.
Table looks like this.
$data = $data->sort(function($a, $b) use ($sortBy) {
if ($a->{$sortBy} and $b->{$sortBy}) return 0;
return ($a->{$sortBy} > $b->{$sortBy}) ? -1 : 1;
});
Random code I tried from the internet, which I can't get to work correctly.
$sortBy contains a field name to sort by ( since it may change )
Faulty code deals with empty / null data but its out of order.
Have to use sort() with a closure. Below will sort timestamp ASC with NULL at the end.
$sorted = $collection->sort(function ($a, $b) {
if (!$a->timestamp) {
return !$b->timestamp ? 0 : 1;
}
if (!$b->timestamp) {
return -1;
}
if ($a->timestamp == $b->timestamp) {
return 0;
}
return $a->timestamp < $b->timestamp ? -1 : 1;
});
Try:
$collection->sortBy('-timestamp')
Does it work?
I had a similar issue. In my case, the time attribute of a $result might be NULL. It acts as if NULL is 0 (as int) and that's expected behavior. But I also wanted to sort the collection by leaving NULL last.
$collection->sortBy(function ($result) {
if ($result['time'] === NULL) {
return PHP_INT_MAX;
}
return $result['time'];
});
You can achieve this simply by returning an value higher in the alphabetical order compared to all other values in the array. i.e. PHP_INT_MAX to be safe. This will make sure all the results where the time equals NULL are at the end of the array.
Similar to Mr. Speelman's solution, but as a shorter PHP 7.4+ version:
$collection->sortBy(fn($e) => $e->timestamp ?: PHP_INT_MAX)
I assume your timestamp is unix timestamp.
You can sort it like this :
$sorted = $collection->sortByDesc('timestamp');
Related
This question already has answers here:
Sort multidimensional array by multiple columns
(8 answers)
Closed 2 months ago.
In one data I have a field title and field name ( field name is the name of a pdf file uploaded, for ex : My_file.pdf and title field is like an override for the front end, a null field)
I want to sort the data in alphabetical order taking into account the name field and the title field
Actually i tried to sort only by name like this :
usort($filesContent, function($a, $b) {
return strcmp($a->getName(), $b->getName());
});
But now I'm stuck, I don't see how to add a filter on the title
can someone guide me to solve my problem ? thanks
You could concatenate both name and title into one string and compare... This would work for files with same filename but different titles. However this may have a weird effect with some data.
usort($filesContent, function($a, $b) {
return strcmp($a->getName().$a->getTitle(), $b->getName().$b->getTitle());
});
You'd best sort by name first, then if the comparison is returning 0 meaning they are the same, sort by title.
usort($filesContent, function($a, $b) {
$nameSort = strcmp($a->getName(), $b->getName());
if($nameSort == 0) return strcmp($a->getTitle(), $b->getTitle());
return $nameSort;
});
You callback function can do whatever you want. It only return value that matters. You need to return -1 when $a is "smaller" (whatever that means for you) than $b, 1 if $a is "greater" than $b or 0 if both are equal. So you basically with two sort keys, you sort by first and if you got 0 then you sort by second key.
usort($filesContent, function($a, $b) {
$result = strcmp($a->getName(), $b->getName());
return $result === 0
? strcmp($a->getTitle(), $b->getTitle())
: $result;
});
Although, I have successfully implemented Google Keyword Planner API to generate Keyword Ideas in PHP with the link below.
https://developers.google.com/google-ads/api/docs/keyword-planning/generate-keyword-ideas
Does anyone know the fastest way to sort the result by AvgMonthlySearches?
// Iterate over the results and print its detail.
foreach ($response->iterateAllElements() as $result) {
/** #var GenerateKeywordIdeaResult $result */
// Note that the competition printed below is enum value.
// For example, a value of 2 will be returned when the competition is 'LOW'.
// A mapping of enum names to values can be found at KeywordPlanCompetitionLevel.php.
printf(
"Keyword idea text '%s' has %d average monthly searches and competition as %d.%s",
$result->getText(),
is_null($result->getKeywordIdeaMetrics()) ?
0 : $result->getKeywordIdeaMetrics()->getAvgMonthlySearches(),
is_null($result->getKeywordIdeaMetrics()) ?
0 : $result->getKeywordIdeaMetrics()->getCompetition(),
PHP_EOL
);
}
Thanks
You could implement a function that can compare two instances of your object and use usort:
function cmp($a, $b) {
return $a->getKeywordIdeaMetrics()->getAvgMonthlySearches() - $b->getKeywordIdeaMetrics()->getAvgMonthlySearches();
}
$list = iterator_to_array($response->->iterateAllElements());
usort($list, "cmp");
// $list will be your sorted array to work with from here onwards ...
See more:
Sort array of objects by object fields
https://www.php.net/manual/en/function.iterator-to-array.php
There are a couple of similar questions here on SO, but I still haven't found an answer.
PHP manual states that
The comparison function must return an integer less than, equal to, or
greater than zero if the first argument is considered to be
respectively less than, equal to, or greater than the second.
The thing is that I don't understand why is it so important to have three optional outcomes, like -1, 0 and 1? Why wouldn't it work out if I used 0 for cases when two values being compared are equal and 1 otherwise?
A more practical question: I have a task where I should find elements of the first array that exist in the second one. The structures of the arrays are the same and look like the following:
Array
(
[0] => Array
(
[productId] => 5479046275
[options] => Array
(
[1] => All
[2] => Green
)
)
)
So I consider elements of such arrays equal when productId values match, and when there's no difference between options respecting their keys. Comparing options should be performed with the fact that 'All' options match every other's value. So I have a code like this:
$cartItems = <MyArray>;
$triggers = <MyAnotherArray>;
/**
* Options equal each other when they are exactly the same or
* when bundle offer's product has 'All' selected as option.
*/
$compareOptions = function ($a, $b) {
if ('All' == $b) {
return 0;
}
return strcmp($a, $b);
};
/**
* Compare product id to make sure these variants are of the same product
* Then check the options. If there's no difference, then variants
* are equal
*/
$compareVariants = function ($a, $b) use ($compareOptions) {
if ($a['productId'] != $b['productId']) {
return ($a['productId'] > $b['productId']) ? 1 : -1;
}
$optionsDiff = array_udiff_assoc($a['options'], $b['options'], $compareOptions);
if (0 === count($optionsDiff)) {
return 0;
} else {
return (count($optionsDiff) > 0) ? 1 : -1;
}
};
return array_uintersect($cartItems, $triggers, $compareVariants);
Then I xDebugged it. For some reason, when $optionsDiff is empty and script returns 0, it doesn't not step out of $compareVariants function, but goes to $compareOptions comparing arrays which produces a error.
Why does it work like that?
Thanks.
array_udiff_assoc() will compare the keys using built-in function and values will be compared using user-defined function.
array_udiff_assoc($array1,$array2,"userDefinedFunction");
So no matter what the result is. The user defined function will be called automatically to compare the array values. If you don't want to call your 'compareOptions' function, then you can go for array_diff_assoc($array1,$array2). This will use built in function to compare both keys and values of the arrays.
I would like to sort an array of objects by a property of the specific object. This is my array with objects:
As you can see I have an array $all_studies with 2 objects. How can I now sort on the graduationYear property of the objects? So I would like to have an array with objects and the the order with object 2010 first, then 2014, ... (in this case the order is already correct but this won't always be the same ..).
This is what I've tried but with no succes:
$all_studies = usort($all_studies, "sort_objects_by_graduationyear");
function sort_objects_by_graduationyear($a, $b) {
if((int)$a->graduationYear == (int)$b->graduationYear){ return 0 ; }
return ($a->graduationYear < $b->graduationYear) ? -1 : 1;
}
But I just get true back. I've never used the usort function so I don't really know how to work with it. Can someone help me?
The function usort returns "true" on success. So, good news :).
If you want to check if the sort is done, you only have to check your $all_studies object after the usort.
$status = usort($all_studies, "sort_objects_by_graduationyear");
print_r($all_studies);
You were assigning the value of usort to $all_studies which'll be true and false thus you were not getting the value as desired. In fact you need to just sort the array and print that values and its all done
Try as
usort($all_studies, "sort_objects_by_graduationyear");
function sort_objects_by_graduationyear($a, $b) {
if((int)$a->graduationYear == (int)$b->graduationYear){ return 0 ; }
return ($a->graduationYear < $b->graduationYear) ? -1 : 1;
}
print_r($all_studies);
Return Values ΒΆ
Returns TRUE on success or FALSE on failure.
Check Docs
I export from MYSQL db different info that I put inside an array:
$info=array(
(ID,CALORIES,PROTEIN,CARBOS,FIBER),
...
);
ex: (1,270,24,12,5),(2,280,2,42,10),...
Then, further down the script, I need to get the IDs of the 3 products with the highest calories, of the 6 products with the highest result of 2xPROTEIN+3xCARBOS, etc for 5 charts.
How can I do such a sorting of the array to fill my different tables?
The function sort() only seems to work for a single-dimensioned array (if it works for my case, I don't know what would be the syntax). It also doesn't seem to work for my more advanced sortings (2*x+3*y)...
Even tho it is not exactly what you are asking, I would highly suggest you preparing your tables in mysql (using all formulas to sort, etc.). Mysql's main job is do various selections and sorting, I would leave the work for it. Moreover, advanced sorting in mysql is way easier than thinking of algorithms or functions in php :)
SELECT * FROM `products`
ORDER BY (2*PROTEIN+3*CARBOS) DESC
Easy as that and no headaches. Apply LIMIT 3 at the end to get the top 3. Update
SELECT * FROM `products`
to your more advanced query. If having difficulties in code you may try to wrap it up as a subquery as this:
SELECT * FROM (SELECT * FROM `products` WHERE `type`='fruit' LIMIT 6) a
ORDER BY (2*PROTEIN+3*CARBOS) DESC LIMIT 3
You can use array_multisort() to sort multidimensional arrays. The syntax allows for quite some flexibilty, see the reference.
You can use usort function:
function cmp( $a, $b )
{
if( $a["calories"] *3+ $a["protein"]*2 == $b["calories"] *3+ $b["protein"]*2){ //do another comparison etc.. ; }
return ($a["calories"] *3+ $a["protein"]*2< $b["calories"] *3+ $b["protein"]*2)) ? -1 : 1;
}
usort($myarray,'cmp');
usort is your friend here.
function sort2Protein3Carbo($a, $b)
{
// Assuming protein is the 2nd value in your array and carbo the 3rd.
$resA = 2 * $a[2] + 3 * $a[3];
$resB = 2 * $b[2] + 3 * $b[3];
if ($resA == $resB)
return 0;
return ($resA < $resB) ? -1 : 1;
}
usort($info, "sort2Protein3Carbo");