Sort array with objects on property of objects - php

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

Related

How to sort Google Generated Keyword Ideas 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

Merging objects in PHP based on a property [duplicate]

This question already has answers here:
Merging and group two arrays containing objects based on one identifying column value
(4 answers)
Closed last month.
What I am originally trying to do is merge two objects
I have one object like this
{#23 ▼
+"Name": "Consultation"
+"ServiceId": "1024"
+"Price": ".00"
}
Another object
{#362 ▼
+"StartTime": "9:15AM"
+"ServiceId": "1024"
}
I am looking for a way to merge these two objects based on it's ServiceId, so I get a single object like this
{#1 ▼
+"Name": "Consultation"
+"ServiceId": "1024"
+"Price": ".00"
+"StartTime": "9:15AM"
}
Of course, if the ServiceId between the two objects do not match, it should not merge.
Any ideas on how to solve the error?
There seems to be two parts to your question. The first part is how to merge the output of two objects returned by json_decode. The second part is how to merge them only if the ServiceId match.
Part 1
json_decode produces, by default, objects of class \StdClass. If you want merge two \StdClass objects into a third \StdClass object, you need some judicious casting:
$a = json_decode('{"Name":"Consultation", "ServiceId":"1024", "Price":".00"}');
$b = json_decode('{"StartTime": "9:15AM", "ServiceId": "1024"}');
$c = (object)array_merge((array)$a, (array)$b);
var_dump($c);
Here, $a and $b are \StdClass objects. Cast them to array, merge, then cast them back to \StdClass.
That's a bit of a round-about way to go. So, you can benefit from working with these as arrays from the get-go. json_decode takes an optional second argument, which instructs it to return an array:
$a = json_decode('{"Name":"Consultation", "ServiceId":"1024", "Price":".00"}', true);
$b = json_decode('{"StartTime": "9:15AM", "ServiceId": "1024"}', true);
$c = array_merge($a, $b);
var_dump($c);
This works in arrays the whole time. If you later want $c to be a \StdClass object, you can cast it using $c = (object)$c; as was done in the first example.
See these live on 3v4l.org.
Part 2
Presumably, you're going to need some logic that iterates or otherwise pairs these objects together. You don't mention the source, but the logic will look something like this:
if ($a->ServiceId == $b->ServiceId) {
$c = (object)array_merge((array)$a, (array)$b)
}
If you have a list of objects, and you want to merge them all together, you can use the combinatoric array walk behavior of usort:
$merged = [];
usort($objects, function ($a, $b) use ($merged) {
$comp = strcmp($a->ServiceId, $b->ServiceId);
if (0 === $comp) {
$merged[] = (object)array_merge((array)$a, (array)$b)
}
return $comp;
});
var_dump($merged);
This iterates through your list of objects, comparing each. If the ServiceId match, then it merges the two objects and adds it to a list of merged. This will happen for as many objects that share ServiceId as you have in your list.
The idea here is too loop through 1 array and find the (assuming 1) element in the other array that match the current one:
foreach ($array1 as $key => $item) {
$other = array_filter($array2, function ($item2) use ($item) {
return $item2->ServiceId == $item->ServiceId;
});
if (!empty($other)) {
$item->StartTime = current($other)->SomethingElse;
}
}
Because you're dealing with objects merging all properties is a bit tricky.
Here's an example:
http://sandbox.onlinephpfunctions.com/code/15a9710dea77672c4ac4165044ad2c7aae4ae234

Sorting laravel collection by leaving null/empty last

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');

How do array_udiff(), array_uintersect() etc. work?

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.

Why is this PHP code to sort an array not returning the correct value?

I have an array of products that I'm pulling from the db. I've verified that these are correct, using the following code:
$unsorted_products = products::get( array( 'collection' => $collection->id ) );
die(print_r($unsorted_products));
...and the results are as I expect. The thing is, I need the results to basically be grouped by a parent category and category. How they're sorted within each grouping, I don't care. Given that I don't have access to change the model that is retrieving the data via SQL, I need to sort this array via PHP. I'm using the following code:
$products = usort($unsorted_products, function ($a, $b) {
return strcmp(
$a->parentcategory.$a->categoryname,
$b->parentcategory.$b->categoryname
);
});
...but dumping the $products array reveals that it is only holding the value 1. Any idea what I am doing wrong? I've verified that the properties I am attempting to access do exist in each object.
Thanks.
It sorts the input array.
From the usort manual:
Returns TRUE on success or FALSE on failure.
You are doing it wrong. usort doesn't return array, it returns ether true or false. It changes array that you pass as first parameter.
After your code is executed, $unsorted_products becomes sorted array. Do this:
$products = products::get( array( 'collection' => $collection->id ) );
and this:
usort($products, function ($a, $b) {
return strcmp(
$a->parentcategory.$a->categoryname,
$b->parentcategory.$b->categoryname
);
});

Categories