i have a booking system that uses for bookings an array in this form:
array(6) {
[0]=> array(5) {
["date"]=> string(10) "08/12/2016"
["start"]=> string(5) "15:00"
["end"]=> string(5) "16:00"
["booked_by"]=> string(5) "mark"
["id"]=> string(1) "4"
}
I used this function to sort it by date and to get all bookings in an array:
function getBooking($aula) {
global $client;
$keys = $client->keys("Calendar:aula$aula:*");
$booking = array();
foreach ($keys as $key => $value) {
$result = $client->hGetAll($value);
array_push($booking, $result);
}
usort($booking, function($a1, $a2) {
$v1 = strtotime(str_replace("/", "-", $a1['data']));
$v2 = strtotime(str_replace("/", "-", $a2['data']));
return $v1 - $v2; // $v2 - $v1 to reverse direction
});
return $booking;
}
I need to sort it also by "start" key to show them in a calendar. I tried adding another usort but it doesn't work. If you need more info please write me back
Thanks in advance for you help
As #Don't Panic says, if you can do the sorting as part of your database request go for that.
Otherwise, if you are stuck with the array as is - you can extract the data from the date and start strings, put it in the correct order, then use strcmp() when you sort. For example,
usort($booking, function($a, $b) {
// extract year, month and day from date
list($a_month, $a_day, $a_year) = explode('/', $a['date']);
list($b_month, $b_day, $b_year) = explode('/', $b['date']);
// compare the correctly ordered strings
return strcmp($a_year.$a_month.$a_day.$a['start'], $b_year.$b_month.$b_day.$b['start']);
});
Some reference if some of the code is unfamiliar
strcmp() http://php.net/manual/en/function.strcmp.php
list() http://php.net/manual/en/function.list.php
The #Steve solution is great. But you can also use array_multisort and array_column:
array_multisort(
array_map('strtotime', array_column($booking, 'date')),
array_column($booking, 'start'),
$booking
);
Here is working demo.
While in this particular situation it is not optimal, it can be more suitable when you need, for example, to sort by date ascending, but by start descending, or otherwise.
Nevertheless, such operations better to be done inside the database.
EDIT
As #Don't Panic said in the comments, the sorting by date can be tricky because of format. To compensate for this we can array_map 'date' column with strtotime.
Related
I want to create a function to print a list of 50 random floats from 0 to 1.
The function to print one random float is simply :
function random_from_0_to_1()
{
return (float)rand() / (float)getrandmax();
}
But how do I get a list of 50 numbers in descending order?
I want to use usort() function, but I am not sure how to use it with a list of 50 random floats.
Generate a array of floats, sort with sort(), then reverse the array to give descending order.
So, using your function:
<?php
function random_from_0_to_1()
{
return (float)rand() / (float)getrandmax();
}
$arr = [];
for ($i=0;$i<50;$i++) {
$arr[] = random_from_0_to_1();
}
sort($arr); // sorts ascending
$arr = array_reverse($arr);
var_dump($arr);
Output:
array(50) {
[0]=>
float(0.9991139778863238)
[1]=>
float(0.9733540797482031)
[2]=>
float(0.9620095835821748)
[3]=>
float(0.9390542404442347)
[4]=>
float(0.9368096925023989)
[5]=>
float(0.9321818514411253)
[6]=>
float(0.9321091510039331)
...
Demo: https://3v4l.org/NJvGu
[Edit]
Since you've specifically asked for a version with usort(), try this, which substitutes usort() for sort() and array_reverse():
<?php
function random_from_0_to_1()
{
return (float)rand() / (float)getrandmax();
}
$arr = [];
for ($i=0;$i<50;$i++) {
$arr[] = random_from_0_to_1();
}
usort($arr, function($a,$b){return $b<=>$a;}); // Note parameters reversed in spaceship comparison
var_dump($arr);
Demo: https://3v4l.org/qn7Ka
I have an array of timestamps from which I want to extract the years. I know how to do it by looping the array but I was wondering if there's a function that returns me an array with the years.
Example of my array
$dates = ['1498780800000', '1530316800000', '1561852800000', '1593475200000'];
Expected output
magic_function($dates); // ['2017', '2018', '2019', '2020'];
Your timestamps are millisecond timestamps generated from Javascript. For use in PHP these have to be converted into seconds in order to then determine the year with the date function. To create an array of the years without a foreach loop, array_map can be used.
function getYearsFromMsTimestamps(array $msTimestamps){
return array_map(function($val){return date('Y',$val/1000);},$msTimestamps);
}
$dates = ['1498780800000', '1530316800000', '1561852800000', '1593475200000'];
$arrYears = getYearsFromMsTimestamps($dates);
$expected = ['2017', '2018', '2019', '2020'];
var_dump($arrYears === $expected); //bool(true)
A more flexible approach would be to use a format as the second optional parameter for the function.
function getYearsFromMsTimestamps(array $msTimestamps, $format = 'Y'){
return array_map(
function($val) use($format){
return date($format,$val/1000);
},
$msTimestamps
);
}
Without a 2nd parameter you get a result like above. With 'Y-m-d' as 2nd parameter
$arrYears = getYearsFromMsTimestamps($dates,'Y-m-d');
then an array like
array(4) {
[0]=> string(10) "2017-06-30"
[1]=> string(10) "2018-06-30"
[2]=> string(10) "2019-06-30"
[3]=> string(10) "2020-06-30"
}
I want to sort elements of an array which have string values (word) according to word length.
I am performing insertion sort:
$str="welcome to php";
$st=explode(" ",$str);
$a=count($st);
for($i=0;$i<$a;$i++)
{
for($j=0;$j<$a;$j++)
{
if(strlen($st[$j])<strlen($st[$j+1]))
{$t=$st[$j];
$st[$j]=$st[$j+1];
$st[$j+1]=$t;}
}}
So the problem is $st[$j+1]. It doesn't get next value of array. It gives undefined offset. How can I get next value of array?
Somthing like this should work for you:
<?php
function lengthSort($a, $b){
return strlen($b) - strlen($a);
}
$str = "welcome to php";
$st = explode(" ", $str);
usort($st,'lengthSort');
var_dump($st);
?>
Output:
array(3) {
[0]=>
string(7) "welcome"
[1]=>
string(3) "php"
[2]=>
string(2) "to"
}
Also as Nick J suggested take a look at the foreach loop! It's very powerful!
You can define your own comparison function using php's usort function.
See http://php.net/manual/en/function.usort.php
So all you have to do is write your own function that compares strings by their length, then call usort and pass in your function.
This question already has answers here:
How to check if PHP array is associative or sequential?
(60 answers)
Closed last year.
I'd like to be able to pass an array to a function and have the function behave differently depending on whether it's a "list" style array or a "hash" style array. E.g.:
myfunc(array("One", "Two", "Three")); // works
myfunc(array(1=>"One", 2=>"Two", 3=>"Three")); also works, but understands it's a hash
Might output something like:
One, Two, Three
1=One, 2=Two, 3=Three
ie: the function does something differently when it "detects" it's being passed a hash rather than an array. Can you tell I'm coming from a Perl background where %hashes are different references from #arrays?
I believe my example is significant because we can't just test to see whether the key is numeric, because you could very well be using numeric keys in your hash.
I'm specifically looking to avoid having to use the messier construct of myfunc(array(array(1=>"One"), array(2=>"Two"), array(3=>"Three")))
Pulled right out of the kohana framework.
public static function is_assoc(array $array)
{
// Keys of the array
$keys = array_keys($array);
// If the array keys of the keys match the keys, then the array must
// not be associative (e.g. the keys array looked like {0:0, 1:1...}).
return array_keys($keys) !== $keys;
}
This benchmark gives 3 methods.
Here's a summary, sorted from fastest to slowest. For more informations, read the complete benchmark here.
1. Using array_values()
function($array) {
return (array_values($array) !== $array);
}
2. Using array_keys()
function($array){
$array = array_keys($array); return ($array !== array_keys($array));
}
3. Using array_filter()
function($array){
return count(array_filter(array_keys($array), 'is_string')) > 0;
}
PHP treats all arrays as hashes, technically, so there is not an exact way to do this. Your best bet would be the following I believe:
if (array_keys($array) === range(0, count($array) - 1)) {
//it is a hash
}
No, PHP does not differentiate arrays where the keys are numeric strings from the arrays where the keys are integers in cases like the following:
$a = array("0"=>'a', "1"=>'b', "2"=>'c');
$b = array(0=>'a', 1=>'b', 2=>'c');
var_dump(array_keys($a), array_keys($b));
It outputs:
array(3) {
[0]=> int(0) [1]=> int(1) [2]=> int(2)
}
array(3) {
[0]=> int(0) [1]=> int(1) [2]=> int(2)
}
(above formatted for readability)
My solution is to get keys of an array like below and check that if the key is not integer:
private function is_hash($array) {
foreach($array as $key => $value) {
return ! is_int($key);
}
return false;
}
It is wrong to get array_keys of a hash array like below:
array_keys(array(
"abc" => "gfb",
"bdc" => "dbc"
)
);
will output:
array(
0 => "abc",
1 => "bdc"
)
So, it is not a good idea to compare it with a range of numbers as mentioned in top rated answer. It will always say that it is a hash array if you try to compare keys with a range.
Being a little frustrated, trying to write a function to address all combinations, an idea clicked in my mind: parse json_encode result.
When a json string contains a curly brace, then it must contain an object!
Of course, after reading the solutions here, mine is a bit funny...
Anyway, I want to share it with the community, just to present an attempt to solve the problem from another prospective (more "visual").
function isAssociative(array $arr): bool
{
// consider empty, and [0, 1, 2, ...] sequential
if(empty($arr) || array_is_list($arr)) {
return false;
}
// first scenario:
// [ 1 => [*any*] ]
// [ 'a' => [*any*] ]
foreach ($arr as $key => $value) {
if(is_array($value)) {
return true;
}
}
// second scenario: read the json string
$jsonNest = json_encode($arr, JSON_THROW_ON_ERROR);
return str_contains($jsonNest, '{'); // {} assoc, [] sequential
}
NOTES
php#8.1 is required, check out the gist on github containing the unit test of this method + Polyfills (php>=7.3).
I've tested also Hussard's posted solutions, A & B are passing all tests, C fails to recognize: {"1":0,"2":1}.
BENCHMARKS
Here json parsing is ~200 ms behind B, but still 1.7 seconds faster than solution C!
What do you think about this version? Improvements are welcome!
array(2) {
[0]=>
object(stdClass)#144 (2) {
["id"]=>
string(1) "2"
["name"]=>
string(5) "name1"
}
[1]=>
object(stdClass)#145 (2) {
["id"]=>
string(1) "4"
["name"]=>
string(5) "name2"
}
}
I want to add key and value (for example [distance] = 100;) to the objects in the array. After this I want to sort on the distance values. How can I achieve this?
To get structure such as yours you can do:
$arr = array();
$arr[0]->id = "2";
$arr[0]->name = "name1";
$arr[1]->id = "4";
$arr[1]->name = "name2";
To add "distance" you can do:
$arr[0]->distance = 100;
$arr[1]->distance = 200;
To sort you can use decorate/sort/undecorate pattern:
$arr = array_map(create_function('$o', 'return array($o->distance, $o);'), $arr); // transform array of objects into array of arrays consisted of sort key and object
sort($arr); // sort array of arrays
$arr = array_map('end', $arr); // take only last element from each array
Or you can use usort() with custom comparison function like this:
function compareDistanceFields($a, $b) {
return $a->distance - $b->distance;
}
usort($arr, "compareDistanceFields");
$my_array[0]->distance = 100;
$my_array[0]->distance = 101;
usort($my_array, "cmp");
function cmp($a, $b)
{
if ($a->distance == $b->distance)
return 0;
return ($a->distance > $b->distance) ? 1: -1;
}
What you have here is an array of hashes; that is, each element of your array is a hash (a structure containing elements, each of which is identified by a key).
In order to add a key and value, you just assign it, like this:
$array[0]["distance"]=100;
$array[1]["distance"]=300;
#and so on
PHP hashes and arrays in general are documented here.
Now, in order to sort your array (each of whose elements is a hash), you need to use the "uasort" function, which allows you to define a comparison function; in this comparison function you define the behavior you want, which is sorting on the value of the distance key.
Something like this:
// Comparison function, this compares the value of the "distance" key
// for each of the two elements being compared.
function cmp($a, $b) {
if ($a["distance"] == $b["distance"]) {
return 0;
}
return ($a["distance"] < $b["distance"]) ? -1 : 1;
}
Once this is defined, you call uasort like this:
uasort($array, 'cmp');
Find more documentation on uasort here.