Sort array of objects by date field - php

How can I re-arrange a array of objects like this:
[495] => stdClass Object
(
[date] => 2009-10-31 18:24:09
...
)
[582] => stdClass Object
(
[date] => 2010-2-11 12:01:42
...
)
...
by the date key, oldest first ?

usort($array, function($a, $b) {
return strtotime($a['date']) - strtotime($b['date']);
});
Or if you don't have PHP 5.3:
function cb($a, $b) {
return strtotime($a['date']) - strtotime($b['date']);
}
usort($array, 'cb');

Since the original question is about sorting arrays of stdClass() objects, here's the code which would work if $a and $b are objects:
usort($array, function($a, $b) {
return strtotime($a->date) - strtotime($b->date);
});
Or if you don't have PHP 5.3:
function cb($a, $b) {
return strtotime($a->date) - strtotime($b->date);
}
usort($array, 'cb');

I wanted to expand on arnaud576875's answer. I ran across this same issue, but with using DateTime objects. This is how I was able to accomplish the same thing.
usort($array, function($a, $b) {
return $a['date']->format('U') - $b['date']->format('U');
});

Because your month -- and possibly your day -- values are not zero-padded, you cannot instantly compare the dates as simple strings. You should use strtotime() to convert the dates to unix time integers -- these will be suitable for reliable comparisons.
Also, it seems important to not lose the associative relationship between the first level keys and their objects. To sort and retain the keys, call uasort(). In modern php, the spaceship operator is the go-to utility for making 3-way comparisons (returns -1, 0, or 1).
Code:
uasort(
$array,
function($a, $b) {
return strtotime($a->date) <=> strtotime($b->date);
}
);
Or in PHP7.4, there is arrow function syntax:
uasort(
$array,
fn($a, $b) => strtotime($a->date) <=> strtotime($b->date)
);
The only minor drawback with using function calls in u*sort()'s body is that it will do greater than n sets of function calls to break ties and otherwise determine the correct order. An alternative sorting technique that avoids these redundant function calls is array_multisort(). It can be fed a column of data which has had exactly n function calls performed -- this effectively makes it more efficient. However, this sorting function has its own caveat -- it will lose the numeric first level keys. This is probably not a tolerable loss for this case.
Code:
array_multisort(
array_map('strtotime', array_column($array, 'date')),
$array
);
Here is a demo of both techniques.
For anyone who is sorting date, time, or datetime values that can be naturally compared as basic strings (so-called Big-endian formats such as Y-m-d H:i:s, H:i, m/d, Y-m-d, etc., then see this answer for more efficient techniques.

I wanted to expand on arnaud576875 and Michael Irigoyen.
Same issue with object containing dateTime with Symphony.
I coudn't use $a['date'] because it was not an key array.
usort($verifications, function($a, $b) {
return $a->getDate()->format('U') - $b->getDate()->format('U');
});
This solved my problem

When working with DateTime objects as properties of your Entity, this worked for me:
$myArray = $entityRepository->findAll();
usort($myArray, function($a, $b) {
return $a->getDate()->getTimestamp() - $b->getDate()->getTimestamp();
});

Related

Usort on object returns wrong order

I need to modify an array of objects, based on a "custom_sorting" value. I use usort like this:
usort($this->rows, function($a, $b) {
return $a->custom_sorting <=> $b->custom_sorting;
});
However, the returned sort order is a bit off, as can be seen in the example order below. Notice 4 comes after 39, so it treats 4 as a higher value than 39. Same goes for 5,6,7,8,9 - all treated as higher values than 45. What can I do to sort it in the correct numeric order?
5-37
5-38
5-39
5-4
5-40
5-41
5-42
5-43
5-44
5-45
5-5
5-7
5-8
5-9
Thanks
As #Nigel Ren's comment suggests, your custom_sorting is probably based on string, so it's working as it should. Instead try:
Newer answer using the built in strnatcmp:
usort($this->rows, function($a, $b) {
return strnatcmp($a->custom_sorting, $b->custom_sorting);
});
Older, more manual answer:
usort($this->rows, function($a, $b) {
$aDash = strpos($a->custom_sorting, '-');
$bDash = strpos($b->custom_sorting, '-');
$compareFirstPart = ((int) substr($a->custom_sorting, 0, $aDash-1)) <=> ((int) substr($b->custom_sorting, 0, $bDash-1));
if ($compareFirstPart !== 0) {
return $compareFirstPart;
}
return ((int) substr($a->custom_sorting, $aDash)) <=> ((int) substr($b->custom_sorting, $bDash));
});
Explanation:
Take the parts before the dash ('-'), cast them to ints, and comare them as ints.
If they are equal the result is 0, so return the result for the same comparison with the part after the dash;

PHP Get differences in multidimensional array based on second value

I have two multidimensional arrays, each element in the array consists of 2 elements, the first is a string and the second is an integer. I want to get the difference between the two multidimensional arrays based on the second value if and only if the first elements are equal. I am using array_udiff such as below:
$arrdiff = array_udiff($arr1, $arr2, 'udiffCompare');
I implemented the function array_udiff such that if the first element is different to return them as equal since I don't want it to appear in the difference, and if the first element is equal then compare the second element and return accordingly, below is the function I implemented
function udiffCompare($a, $b) {
return strcmp($a[0], $b[0]) == 0 ? $ a[1] - $b[1] : 0;
}
However, even though I have two arrays with the same first element but a different second element, they are not returned in the result of array_udiff function.
Am I missing anything here? Any help is appreciated.
The problem is, you're looking for a difference within an intersection, using only a difference function.
Try computing the intersection based on the string value, and using the result to compute the difference based on the int value.
function sameString ($a, $b) {
return strcmp($a[0], $b[0]);
}
function differentInt($a, $b) {
return $a[1] - $b[1];
}
$diff = array_udiff(array_uintersect($arr1, $arr2, 'sameString'), $arr2, 'differentInt');

How to view the steps of the usort() in PHP?

This is the Example #1 from the php.net usort() page:
<?php
function cmp($a, $b) {
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
$a = array(3, 2, 5, 6, 1);
usort($a, "cmp");
foreach ($a as $key => $value) {
echo "$key: $value\n";
}
?>
The usort function takes the values within the array as pairs ($a-$b; so this is - 3-2, 2-5, 5-6, 6-1) and moves the $b value depending on whether the cmp() function returns -1, 0 or 1. If it is -1 the $b gets moved down (within a current pair), if it is 0 it stays in the same place and if it is 1 it gets moved up. This is how this is suppose to be working based on the top comment from the php.net manual usort() page.
Is there any way to see how this works step by step (the sorting process)? Am I able to see it or is it only possible to see the final result after the sorting is done? I want to understand fully how this process works.
Using some debug output in your comparison function you can only see the comparisons that PHP does, but can't see intermediate states of the array.
But the algorithm used in usort is well known - it's QuickSort ( Which sort algorithms does PHP's usort apply? ).
You can see its vizualisation at http://www.algomation.com/algorithm/quick-sort-visualization (or just google "quicksort algorithm visualization")

Sort an array of dates using usort() and sort() functions [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Sort an array of dates using usort() and sort() functions by the timestamp converted by mktime()
I'm trying to sort an array of dates using sort() and usort() only - without using mktime(). I'm trying to compare month, day and year, but can not get the correct result, plus it gives me a bunch of warnings.
Will appreciate any help.
$dates = array ('10-10-2003', '2-17-2002', '2-16-2003','1-01-2005', '10-10-2004' );
function toTime($date) {
return sort ($date, SORT_STRING);
}
function sortByTime($a, $b) {
$a = toTime($a);
$b = toTime($b);
if($a == $b) {
return 0;
}
return $a < $b ? -1 : 1 ;
}
usort($dates, 'sortByTime');
print_r($dates);
Thank you so much.
use uksort to sort by keys with a callback
in the callback simply parse the date to a timestamp and use a simple comparison
Sudo Code :
function cmp($a, $b)
{
global $array;
return strcmp($array[$a]['db'], $array[$b]['db']);
}
uksort($array, 'cmp');

array intersect for object array php

I want to know how to array_intersect for object array.
You can use array_uintersect in conjunction with spl_object_hash, see an example:
array_uintersect($a, $b, function($a, $b) {
return strcmp(spl_object_hash($a), spl_object_hash($b));
});
where '$a' and '$b' are arrays of some objects that you want to intersect.
nice toString function is already implemented and is called serialize ;) so
array_map(
'unserialize',
array_intersect(
array_map(
'serialize',
$obj1
),
array_map(
'serialize',
$obj2
)
)
);
will do the work, example mentioned higher don't work 'cause array_intersect work's only with strings as someone mentioned too
array_intersect() returns an array containing all the values of array1 that are present in all the arguments.
Then what mean present in this context (exacly this function), i found on php.net my answer:
Two elements are considered equal if
and only if (string) $elem1 ===
(string) $elem2. In words: when the
string representation is the
same.
Then you can't use it on array of objects if your objects not implements unique conversion to string.
Had a similar problem a few days ago, while these are answers are on the right path; I used them to work out the following:
From Artefacto's answer return $obj1 == $obj2 didn't really work, so I wrote a simple comparative function (basically gets the md5 of the serialised object and compares that):
function object_compare($obj1, $obj2){
$md5 = function($obj){
return md5(serialize($obj));
};
return strcmp($md5($obj1), $md5($obj2));
}
Then it’s jut a matter of calling array_uintersect with our comparative function to get the intersection:
# $array1 / $array2 are the array of objects we want to compare
return array_uintersect($array1, $array2, 'object_compare');
In my case, I had an unknown / dynamic array of objects, so I took it a step further so I don't have to declare array_uintersect($array1, $array2, ...) specifically - but just be able to pass in an array of arrays (of objects):
# $multiarray_of_objects is our array of arrays
$multiarray_of_objects[] = 'object_compare';
return call_user_func_array('array_uintersect', $multiarray_of_objects);
Just gotta remember to pass in the reference to our callback / comparative function as the last string in the array. Works like a charm!
The correct way to check whether two objects are equal is to use ==. Therefore:
array_uintersect($arr1, $arr2, function ($a1, $a2) { return $a1 == $a2; });
Just for completeness: Implement __toString() method in your object returning a unique value. For database entities this might be as easy as returning the fully qualified class name postfixed with the ID of the record. But it can also be arbitrarily complex by doing some hashing or even worse things.
In my opinion, it's the class's duty to serialize itself or create something unique to compare its objects by. Using anything outside of a class to serialize an object might result in strange behaviour (including comparing objects of different classes, which must never result in equality).
I use array_udiff to implement array_intersect for an object array.
function diff($a, $b) {
if($a === $b) {
return 0;
} else {
return 1;}
}
$array_1 = array('a', 'b', 'c');
$array_2 = array('c', 'd','e');
$array = array_udiff($array_1, array_udiff($array_1, $array_2, 'diff'),'diff');
var_dump($array);
return array(1) { [2]=> string(1) "c" }
You can have your own diff function for any scheme.
The correct solution would be:
array_uintersect($arr1, $arr2, function ($a1, $a2) { return $a1 != $a2; });
Note the != in the callback function as opposed to the answer from #Artefacto. Based on the documentation of array_uintersect, the callback function has to return 0 (false) if array items are equal.

Categories