Keys in PHP array are not sorted numerically - php

I have a PHP array with keys that contain a year and week number like so:
year-week
Using the built in ksort function it's returning them like so:
ksort($array);
2011-21
2011-3
2011-44
2011-45
Is it possible to have them sorted numerically like so:
2011-3
2011-21
2011-44
2011-45

If you are using PHP >= 5.4 use ksort($array, SORT_NATURAL);

Use uksort to sort the keys, and in the callback use, strnatcmp.
uksort($array, function($a,$b){
return strnatcmp($a,$b);
});

You can use natsort
$a = array_keys($myarray);
// $a now has
// array('2011-21', '2011-3', '2011-45', '2011-44');
natsort($a);
This prints
2011-3
2011-21
2011-44
2011-45
You can then use the $a array as a reference to each element of the array that holds the data (in the example above $myarray)

You can use ksort with natural flag. (Only PHP 5.4+ supports)
ksort($array, SORT_NATURAL);

You'll get the result you want if you format them with a 2 digit week. Something more like 2011-03. See sprint_f().

I see much simpler solutions are available, but here was my initial thought:
function cmp($a, $b) {
$comp1 = explode('-', $a);
$comp2 = explode('-', $b);
$year1 = (int) $comp1[0];
$year2 = (int) $comp2[0];
$week1 = (int) $comp1[1];
$week2 = (int) $comp2[1];
if ($year1 == $year2 && $week1 == $week2) {
return 0;
} elseif ($year1 == $year2) {
return ($week1 < $week2) ? -1 : 1;
} else {
return ($year1 < $year2) ? -1 : 1;
}
}
$array = array('2011-21', '2011-3', '2011-44', '2011-45');
uasort($array, 'cmp');

Related

Sort array of time strings naturally

I need to sort an array of time strings (which are not zero padded) naturally.
Sample data:
$totlahourdat1 = ["9:30", "15:00", "13:00"];
When I try array_multisort($totlahourdat1, SORT_DESC), the data appears unchanged.
My actual code:
while($row11 = mysqli_fetch_array($hourget))
{
$totlahourdat1[] = strtotime($row11['hours']);
}
array_multisort($totlahourdat1, SORT_DESC);
foreach ($totlahourdat1 as $time) {
$totlahourdat[] = date("h:i",$time);
}
echo "<pre>";
var_dump($totlahourdat);
echo "</pre>";
Ultimately, the array data should be ordered from earliest time to latest time:
["9:30", "13:00", "15:00"]
Simply do like below:-
$time = array(0=>"9:30",1=>"15:00",2=>"13:00");
function timecompare($a,$b)
{
return strtotime($a) < strtotime($b) ? -1: 1;
}
uasort($time ,'timecompare');
print_r(array_values($time));
Output:-https://eval.in/835353
Use natsort($array) see function definition
You can use usort() to write a custom sorting function.
$times = array("9:30", "15:00", "13:00");
usort($times, function ($a, $b) {
$a = strtotime($a);
$b = strtotime($b);
if ($a == $b) {
return 0;
}
return ($a < $b) ? 1 : -1;
});
If you are using PHP7, you could use the spaceship operator to greatly reduce the size of the sorting function.
$times = array("9:30", "15:00", "13:00");
usort($times, function ($a, $b) {
return strtotime($b) <=> strtotime($a);
});
Your problem is much simpler than you think. you just forgot to use the proper order
Just Order by ASC
array_multisort($totlahourdat1, SORT_ASC);
See demo here (https://eval.in/835356)
You do not need to parse these time expressions with strtotime() or a datetime method. Just sort naturally.
Code: (Demo)
sort($totlahourdat1, SORT_NATURAL);

how to custom sort array by a specific month key

I am sure this has been answered somewhere before but I couldn't find the answer to this exact case...
I know I can use the usort function, but can't figure out the logic to do what I want eventhough it's relatively simple
I have a dynamic area being generated as such:
$details[$pageYear][$pageMonth][] = array(
"id" => $page['id'],
"title" => $page['title']
);
I want the array $details to be eventually sorted descending by year, then by month
the month value is string (january, february, march, etc... and not numeric), which seems to be my main issue (how to sort the month 'strings' by actual order and not letter order)
any help would be greatly appreciated
sorry if this turns out to be a duplicate
can you use uasort whit this callback?
<?php
function cmp_month_strings($a_string, $b_string)
{
$a_value = strtotime("{$a_string} 2000");
$b_value = strtotime("{$b_string} 2000");
if($a_value == $b_value)
return 0;
else if($a_value < $b_value)
return -1;
else
return 1;
}
?>
To sort by key, you need uksort
Some similar way:
uksort($details, function($a, $b) {
return ($a < $b) ? -1 : 1;
});
foreach ($details as &$year) {
uksort($year, function($a, $b) {
$montha = (int) date('m', strtotime("{$a} 1 2000"));
$monthb = (int) date('m', strtotime("{$b} 1 2000"));
return ($montha < $monthb) ? -1 : 1;
});
}

Sort in a multi dimentional array

I have this array in PHP:
foreach($rss_items as $item) :
$contenu[] = array (
"auteur" => $author,
"date" => $item->get_date(),
"contenu" => $item->get_content()
);
At the end, my array is composed of 6 parts, which everyone contains the 3 variable above.
My question is: How can I sort the array by date and using the strtotime because it's easier to sort with this function.
strtotime($item->get_date());
Use usort to sort based on your own callback function.
usort($contenu, function($a, $b){
$aSort = strtotime($a['date']);
$bSort = strtotime($b['date']);
if($aSort === $bSort) return 0;
return ($aSort < $bSort) ? -1 : 1;
});
NOTE: This will only work in PHP 5.3+. If you have 5.2 or lower, you can use create_function.
usort($contenu, create_function('$a, $b','
$aSort = strtotime($a["date"]);
$bSort = strtotime($b["date"]);
if($aSort === $bSort) return 0;
return ($aSort < $bSort) ? -1 : 1;
'));

Find number which is greater than or equal to N in an array

If I have a PHP array:
$array
With values:
45,41,40,39,37,31
And I have a variable:
$number = 38;
How can I return the value?:
39
Because that is the closest value to 38 (counting up) in the array?
Regards,
taylor
<?php
function closest($array, $number) {
sort($array);
foreach ($array as $a) {
if ($a >= $number) return $a;
}
return end($array); // or return NULL;
}
?>
Here is a high-level process to get the desired results and work for any array data:
Filter the array keeping on values greater than or equal to the target and then select the lowest remaining value. This is the "best" value (which may be "nothing" if all the values were less) -- this is O(n)
Alternatively, sort the data first and see below -- this is O(n lg n) (hopefully)
Now, assuming that the array is sorted ASCENDING, this approach would work:
Loop through the array and find the first element which is larger than or equal to the target -- this is O(n)
And if the array is DESCENDING (as in the post), do as above, but either:
Iterate backwards -- this is O(n)
Sort it ASCENDING first (see fardjad's answer) -- this is O(n lg n) (hopefully)
Iterate forwards but keep a look-behind value (to remember "next highest" if the exact was skipped) -- this is O(n)
Happy coding.
EDIT typo on array_search
Yo... Seems easy enough. Here's a function
<?php
$array = array(45,41,40,39,37,31);
function closest($array, $number){
#does the array already contain the number?
if($i = array_search( $number, $array)) return $i;
#add the number to the array
$array[] = $number;
#sort and refind the number
sort($array);
$i = array_search($number, $array);
#check if there is a number above it
if($i && isset($array[$i+1])) return $array[$i+1];
//alternatively you could return the number itself here, or below it depending on your requirements
return null;
}
to Run echo closest($array, 38);
Here's a smaller function that will also return the closest value. Helpful if you don't want to sort the array (to preserve keys).
function closest($array, $number) {
//does an exact match exist?
if ($i=array_search($number, $array)) return $i;
//find closest
foreach ($array as $match) {
$diff = abs($number-$match); //get absolute value of difference
if (!isset($closeness) || (isset($closeness) && $closeness>$diff)) {
$closeness = $diff;
$closest = $match;
}
}
return $closest;
}
Do a linear scan of each number and update two variables and you'll be done.
Python code (performance is O(N), I don't think it's possible to beat O(N)):
def closestNum(numArray, findNum):
diff = infinity # replace with actual infinity value
closestNum = infinity # can be set to any value
for num in numArray:
if((num - findNum) > 0 and (num - findNum) < diff):
diff = num - findNum
closestNum = num
return closestNum
Please add null checks as appropriate.
If you really want the value that's "closest" in distance, even if it's a lesser value, try this, which #Jason gets most of the credit for.
Imagine a scenario when you want the closest number to 38.9 in the following:
$array = array(37.5, 38.5, 39.5);
Most of the solutions here would give you 39.5, when 38.5 is much closer.
This solution would only take the next highest value if what you're looking is in the exact middle between two numbers in the array:
function nearest_value($value, $array) {
if (array_search($value, $array)) {
return $value;
} else {
$array[] = $value;
sort($array);
$key = array_search($value, $array);
if ($key == 0) { return $array[$key+1]; }
if ($key == sizeof($array)-1) { return $array[$key-1]; }
$dist_to_ceil = $array[$key+1]-$value;
$dist_to_floor = $value-$array[$key-1];
if ($dist_to_ceil <= $dist_to_floor) {
return $array[$key+1];
} else {
return $array[$key-1];
}
}
}
What it lacks in elegance, it makes up for in accuracy. Again, much thanks to #Jason.
Try this simple PHP function:
<?php
function nearest($number, $numbers) {
$output = FALSE;
$number = intval($number);
if (is_array($numbers) && count($numbers) >= 1) {
$NDat = array();
foreach ($numbers as $n)
$NDat[abs($number - $n)] = $n;
ksort($NDat);
$NDat = array_values($NDat);
$output = $NDat[0];
}
return $output;
}
echo nearest(90, array(0, 50, 89, 150, 200, 250));
?>
I made a shorter function for that:
function nearestNumber($num, $array) {
if(!in_array($num, $array)) $array[] = $num;
sort($array);
$idx = array_search($num, $array);
if(($array[$idx] -$array[$idx-1]) >= ($array[$idx+1] -$array[$idx])) return $array[$idx+1];
else return $array[$idx-1];
}
Works great in my case: $array = array(128,160,192,224,256,320); $num = 203 :)
It's taking the nearest number and if there's the same distance between two numbers (like 208 for my example), the next highest number is used.
+1 to Jason.
My implementation below, but not as brisk
$array = array(1,2,4,5,7,8,9);
function closest($array, $number) {
$array = array_flip($array);
if(array_key_exists($number, $array)) return $number;
$array[$number] = true;
sort($array);
$rendered = array_slice($array, $number, 2, true);
$rendered = array_keys($rendered);
if(array_key_exists(1, $rendered)) return $rendered[1];
return false;
}
print_r(closest($array, 3));
You could use array_reduce for this, which makes it more functional programming style:
function closest($needle, $haystack) {
return array_reduce($haystack, function($a, $b) use ($needle) {
return abs($needle-$a) < abs($needle-$b) ? $a : $b;
});
}
For the rest, this follows the same principle as the other O(n) solutions.
Here is my solution.
$array=array(10,56,78,17,30);
$num=65;
$diff=$num;
$min=$num;
foreach($array as $a){
if( abs($a-$num)< $diff ){
$diff=abs($a-$num);
$min=$a;
}
}
echo $min;

Sort a multi-dimensional array using PHP

My array is created like this:
$c3_array[$c3_count]["box"] = $box;
$c3_array[$c3_count]["subseries"] = $subseries;
$c3_array[$c3_count]["foldertitle"] = $foldertitle;
$c3_array[$c3_count]["uri"] = $uri;
How can I sort the array based on "box" ASC, then based on "foldertitle" ASC?
Thanks!
You could use usort and create your own comparison function. Here is a simple example, that might or might not work, depending on what the actual values in the arrays are, but it should at least give you the idea.
function mysort ($a, $b)
{
if ($a['box'] > $b['box']) return 1;
if ($a['box'] < $b['box']) return -1;
if ($a['foldertitle'] > $b['foldertitle']) return 1;
if ($a['foldertitle'] < $b['foldertitle']) return -1;
return 0;
}
usort($c3_array, 'mysort');
I think array_multisort() is what you need. Check the PHP documentation
Using array_multisort, as in example 3.
$boxes = array();
$foldertitles = array();
foreach($c3_array as $key => $array) {
$boxes[$key] = $array['box'];
$foldertitles[$key] = $array['foldertitle'];
}
array_multisort($boxes, SORT_ASC, $foldertitles, SORT_ASC, $c3_array);

Categories