PHP - sort array by month and year - php

This is my array:
$ar = array(
'Jan-2016' => 1,
'Feb-2016' => 2,
'Mar-2016' => 3,
'Apr-2016' => 4,
'May-2016' => 4,
'Jun-2016' => 4,
'Jul-2016' => 4,
'Aug-2016' => 4,
'Sep-2016' => 4,
'Oct-2016' => 4,
'Nov-2016' => 4,
'Dec-2016' => 4,
'Jan-2015' => 1,
'Mar-2015' => 1);
I want to sort this array by month & year. I've tried below code:
ksort($ar);
But i can't get result as i expected.
I need result as below array:
$ar = array(
'Jan-2015' => 1,
'Mar-2015' => 1,
'Jan-2016' => 1,
'Feb-2016' => 2,
'Mar-2016' => 3,
'Apr-2016' => 4,
'May-2016' => 4,
'Jun-2016' => 4,
'Jul-2016' => 4,
'Aug-2016' => 4,
'Sep-2016' => 4,
'Oct-2016' => 4,
'Nov-2016' => 4,
'Dec-2016' => 4);

uksort is the right function to use :
uksort($ar, function($a1, $a2) {
$time1 = strtotime($a1);
$time2 = strtotime($a2);
return $time1 - $time2;
});
print_r($ar);

Supplement: Sort Order
$sortOrder = true; // ASC
$sortOrder = false; // DESC
uksort($array, function($a, $b) use($sortOrder) {
$timeA = strtotime($a);
$timeB = strtotime($b);
return $sortOrder ? ($timeA - $timeB) : ($timeB - $timeA);
});

Related

Sort an Array twice or fine-tune array using php

Lets assume I have a soccer league array:
$array = [
'teamId1' =>
[
'games' => 7,
'wins' => 2,
'loss' => 4,
'duals' => 1,
'point' => 7
],
'teamId2' =>
[
'games' => 7,
'wins' => 3,
'loss' => 1,
'duals' => 3,
'point' => 12
],
'teamId3' =>
[
'games' => 7,
'wins' => 4,
'loss' => 3,
'duals' => 0,
'point' => 12
],
'teamId4' =>
[
'games' => 7,
'wins' => 6,
'loss' => 0,
'duals' => 1,
'point' => 19
],
'teamId5' =>
[
'games' => 7,
'wins' => 3,
'loss' => 2,
'duals' => 2,
'point' => 11
],
.
.
.
'teamId18' =>
[
'games' => 7,
'wins' => 5,
'loss' => 0,
'duals' => 2,
'point' => 17
],
];
and I want to sort this By POINT DESC; so I did use this function to sort my array by point:
function sortArray($array, $on, $order=SORT_ASC)
{
$new_array = array();
$sortable_array = array();
if (count($array) > 0)
{
foreach ($array as $k => $v)
{
if (is_array($v))
{
foreach ($v as $k2 => $v2)
{
if ($k2 == $on)
{
$sortable_array[$k] = $v2;
}
}
}
else
{
$sortable_array[$k] = $v;
}
}
switch ($order) {
case SORT_ASC:
asort($sortable_array);
break;
case SORT_DESC:
arsort($sortable_array);
break;
}
foreach ($sortable_array as $k => $v)
{
$new_array[$k] = $array[$k];
}
}
return $new_array;
}
$sortByPoint = sortArray($result, 'point', SORT_DESC);
Ok, now I have an array sorted by point, but now I need something like fine tune my array to reorder array in which when two-or more- teams have same point, the team/s with more wins come before other.
I know I can loop through sorted array and do something, but I'm sure there's shorter and tricky way to do that.
Does anyone any idea about that?
Thanks in Advance
It would be easier to write a specific sort for this. Using usort(), you can add layers of complexity to the conditions as you need it in a simple function...
usort($array, function ($a, $b) {
if ( $a['point'] == $b['point'] ){
// Sort by wins (descending)
return $b['wins'] - $a['wins'];
}
// Sort by points descending
return $b['point'] - $a['point'] ;
});
print_r($array);
This sorts the actual array, so output $array.
You could expand it further by saying if the same number of wins, sort by losses etc.

Sort an Array alphabeticaly but move those with 0 values to the end

I need to sort my multidimensional array using two rules.
My array looks like this:
$array = [
['label' => 12, 'countValue' => 5],
['label' => 4, 'countValue' => 78],
['label' => 9, 'countValue' => 5],
['label' => 64, 'countValue' => 0],
['label' => 3, 'countValue' => 60],
['label' => 19, 'countValue' => 0],
['label' => 7, 'countValue' => 5],
];
I need rows with ['countValue'] = 0 to be move to the back and
Sort the rows based on their label value in an ascending direction.
Desired result:
$array = [
['label' => 3, 'countValue' => 60],
['label' => 4, 'countValue' => 78],
['label' => 7, 'countValue' => 5],
['label' => 9, 'countValue' => 5],
['label' => 12, 'countValue' => 5],
['label' => 19, 'countValue' => 0],
['label' => 64, 'countValue' => 0],
];
I have the following code:
public function sortOptionsByName($a, $b)
{
$x = trim($a['label']);
$y = trim($b['label']);
if ($x == '') return 1;
if ($y == '') return -1;
if (is_numeric($x) && is_numeric($y)){
if ($x == $y)
return 0;
return ($x > $y ? 1 : -1);
}
else {
return strcasecmp($x, $y);
}
}
public function sortOptionsByCounts($a, $b)
{
if ($a['countValue'] == $b['countValue']) {
return 0;
}
return ($a['countValue'] < $b['countValue'] ? 1 : -1);
}
Something like...
public function sortOptionsByCountsAndByName($a, $b)
{
if ($a['countValue'] == 0 && $b['countValue'] == 0) {
return -2
}
else {
$this->sortOptionsByName($a, $b)
}
}
First compare values with zero. PHP casts boolean to integer, so you can just subtract to get -1, 0 , 1. And then compare another value when thw 1st comparing returns 0
public function sortOptionsByCountsAndByName($a, $b)
{
$res = ($a['countValue'] == 0) - ($b['countValue'] == 0);
return ($res ? $res : $this->sortOptionsByName($a, $b));
}
You need to sort your data twice, once by name and once by count value. I would suggest to use usort() and implement two custom compare methods:
<?php
function cmpName($a, $b) {
return strnatcmp($a['Name'], $b['Name']);
}
function cmpCountValue($a, $b) {
if ($a['CountValue'] == 0)
return 1;
if ($b['CountValue'] == 0)
return -1;
return cmpName($a, $b);
}
$a[0]['Name'] = 'Bob';
$a[0]['CountValue'] = 0;
$a[1]['Name'] = 'Christal';
$a[1]['CountValue'] = 42;
$a[2]['Name'] = 'Alice';
$a[2]['CountValue'] = 23;
$a[3]['Name'] = 'Jack';
$a[3]['CountValue'] = 1;
$a[4]['Name'] = 'Alex';
$a[4]['CountValue'] = 58;
usort($a, "cmpName");
usort($a, "cmpCountValue");
foreach ($a as $item) {
echo $item['Name'] . ": " . $item['CountValue'] . "<br />";
}
?>
The output is:
Alex: 58
Alice: 23
Christal: 42
Jack: 1
Bob: 0
Online demo: See here
Just bake your sorting rules into two arrays with symmetric elements and separate them with a spaceship operator.
When sorting ASC, false comes before true, so by checking if the countValue is "falsey", true evaluations will be pushed to the back of the pack.
The order of the elements in the sorting callback says:
Prioritize non-zero countValue rows.
When there is a tie on the first rule, break the tie by sorting on label ASC.
Code: (Demo)
usort(
$array,
fn($a, $b) =>
[!$a['countValue'], $a['label']]
<=>
[!$b['countValue'], $b['label']]
);
var_export($array);
Output:
array (
0 =>
array (
'label' => 3,
'countValue' => 60,
),
1 =>
array (
'label' => 4,
'countValue' => 78,
),
2 =>
array (
'label' => 7,
'countValue' => 5,
),
3 =>
array (
'label' => 9,
'countValue' => 5,
),
4 =>
array (
'label' => 12,
'countValue' => 5,
),
5 =>
array (
'label' => 19,
'countValue' => 0,
),
6 =>
array (
'label' => 64,
'countValue' => 0,
),
)

Fill 2d array with rows for missing periods of array

I have an array that contains periods from 1 - 13. Sometimes the array doesn't contain data for all periods and I need to fill in the missing ones, for example:
$array = [
['period' => 7, 'y' => 20],
['period' => 8, 'y' => 20.50],
['period' => 9, 'y' => 7020],
['period' => 10, 'y' => 6520],
['period' => 11, 'y' => 65920],
['period' => 12, 'y' => 62820],
['period' => 13, 'y' => 6120],
];
For this case I need to run a php loop to fill in the missing first 6 periods with 0 y values. I've tried a variety of loops but with no joy.
Desired output:
[
['period' => 1, 'y' => 0],
['period' => 2, 'y' => 0],
['period' => 3, 'y' => 0],
['period' => 4, 'y' => 0],
['period' => 5, 'y' => 0],
['period' => 6, 'y' => 0],
['period' => 7, 'y' => 20],
['period' => 8, 'y' => 20.50],
['period' => 9, 'y' => 7020],
['period' => 10, 'y' => 6520],
['period' => 11, 'y' => 65920],
['period' => 12, 'y' => 62820],
['period' => 13, 'y' => 6120],
]
You can get good semantics with using the standard array methods. For example:
<?php
$in = [
['period' => 7, 'y' => 20],
['period' => 8, 'y' => 20.50],
['period' => 9, 'y' => 7020],
['period' => 10, 'y' => 6520],
['period' => 11, 'y' => 65920],
['period' => 12, 'y' => 62820],
['period' => 13, 'y' => 6120],
];
// collect available periods
$available = array_column($in, 'period');
// calculate missing periods
$missing = array_diff(range(1, 13), $available);
// transform missing to correct format
$addition = array_map(function ($period) { return ['period' => $period, 'y' => 0]; }, $missing);
// add missing to input
$out = array_merge($in, $addition);
// sort by period
usort($out, function ($a, $b) {
return $a['period'] <=> $b['period'];
});
// done
print_r($out);
demo: https://3v4l.org/2fDYW
start by filling the whole array you need with all period with a value of 0.
Then for each period you get in your data array, use the period id to update the value of period in the right place in the array.
Hope it helps...
You can try this
for ($i=1 $<=13, $i++) {
$exits = false;
foreach ($array as $key => $value) {
if ($value['period'] == $key) {
$exits = true;
}
}
if ($exits) {
$array[] = ['period' => $i , 'y' => 0];
}
}
This should solve your problem:
Let's say $p_array is your array that contains periods from 1 - 13.
// get all periods number that is not missed
$not_missed_periods = array();
foreach ($p_array as $p_each)
{
$not_missed_periods[] = $p_each['period'];
}
// loop for checking all valid periods i.e 1-13
for ($i=1; $i<=13; $i++)
{
// check if $i OR period is not in $not_missed_periods
if (!in_array($i, $not_missed_periods)) {
$p_array[] = array('period' => $i, 'y' => 0);
}
}
print_r($p_array);
Assuming your $array is sorted by period.
You can create a new array that copies the content or your $array and set a new content for missing periods.
$new_array = [];
for ($i = 1, $j = 0; $i <= 13; $i++) {
if ($array[$j]['period'] == $i) {
$new_array[] = $array[$j]; //copy contents
$j++;
} else {
$new_array[] = [ 'period' => $i, 'y' => 0 ]; // set new contents
}
}
Use array_column() to generate a lookup array from your input array -- effectively applying associative, numeric, first-level keys without disturbing the original row data.
Then iterate from 1 to 13. If the current iteration's integer is found in the lookup, then push the found row; otherwise push the default row containing the incremented value.
Code: (Demo)
$lookup = array_column($data, null, 'period');
$result = [];
for ($i = 1; $i <= 13; ++$i) {
$result[] = $lookup[$i] ?? ['period' => $i, 'y' => 0];
}
var_export($result);

PHP - More efficient method for sorting alphanumeric keys

NOTE: I edited the example and replaced all values with 1. The values do not matter, only the keys do. The previous values I had written led to a misunderstanding. Sorry.
NOTE: a/b blocks are always continuous. I'm adding this because it wasn't clear. If there is 3a/3b and 5a/5b there will always be 4a/4b and not only 4.
I have arrays that contain numbered keys with leading zeros. Sometimes, these numbered keys have two variations that I distinguish by using the suffixes a and b. The number of keys with or without variations is unknown, however there is never more than 2 digits; i.e. the highest numerical key is '09'.
The problem is that these array keys need to be sorted numerically, but when suffixes are present, those should take precedence. Using ksort() alone does not achieve this.
For example, ksort() gives me this:
$arr = array(
'01' => 1,
'02' => 1,
'03a' => 1,
'03b' => 1,
'04a' => 1,
'04b' => 1,
'05a' => 1,
'05b' => 1,
'06' => 1,
);
But, I need this:
$arr = array(
'01' => 1,
'02' => 1,
'03a' => 1,
'04a' => 1,
'05a' => 1,
'03b' => 1,
'04b' => 1,
'05b' => 1,
'06' => 1,
);
I use some fancy coding gymnastics to get what I want, but it isn't pretty. I'm wondering if there's a better, cleaner way?
Here is what I do.
1) I use ksort() which gives me the first of the two arrays above. (The one which isn't yet what I want.)
2) I create two arrays. One for the 'a' suffixes, the other for the 'b' suffixes.
$arr_a = array();
$arr_b = array();
foreach ($arr as $k => $v) {
if (substr($k, 2) == 'a') {
$arr_a[$k] = $v;
} else if (substr($k, 2) == 'b') {
$arr_b[$k] = $v;
}
}
3) I merge the two suffix arrays.
$arr_suffixes = array_merge($arr_a, $arr_b);
4) I slice up my original array so that I get the part before the suffixes, and the part after the suffixes.
$i = array_search(key($arr_suffixes), array_keys($arr));
$length = count($arr_suffixes);
$arr_before_suffixes = array_slice($arr, 0, $i);
$arr_after_suffixes = array_slice($arr, $i + $length);
5) Using array_merge, I recombine the sliced arrays to create the array I need.
$arr = array_merge($arr_before_suffixes, $arr_suffixes);
$arr = array_merge($arr, $arr_after_suffixes);
Finally, we have the correct $arr. Isn't there a better way to do this? It feels really ugly.
You have no formalized rule. I'll try to guess.
$arr = array(
'01' => 1,
'02' => 1,
'03a' => 1,
'03b' => 1,
'04a' => 1,
'04b' => 1,
'05a' => 1,
'05b' => 1,
'06' => 1,
);
uksort($arr, function($item1, $item2)
{
$last1 = substr($item1, -1);
$last2 = substr($item2, -1);
// one of the items is a number or last letters matches
if (is_numeric($last1) || is_numeric($last2) || $last1 == $last2)
// simple number comparison
return $item1 - $item2;
else
// natural order comparison
return $last1 > $last2 ? 1 : -1;
});
var_dump($arr);
natsort() function will helps you:
$arr = array(
'01' => 1,
'02' => 1,
'03a' => 1,
'03b' => 1,
'04a' => 1,
'04b' => 1,
'05a' => 1,
'05b' => 1,
'06' => 1,
);
$keys = array_keys($arr);
natsort($keys);
$result = array();
foreach ($keys as $key) {
$result[$key] = $arr[$key];
}
print_r($result); // Your expected result
$arr = array(
'01' => 1,
'02' => 2,
'03a' => 3,
'03b' => 6,
'04a' => 4,
'04b' => 7,
'05a' => 5,
'05b' => 8,
'06' => 9,
);
uksort(
$arr,
function($a, $b) {
sscanf($a, '%d%s', $an, $as);
sscanf($b, '%d%s', $bn, $bs);
if ($as === null || $bs === null || $as === $bs) {
return $an - $bn;
}
return strcmp($as, $bs);
}
);
var_dump($arr);

How to sum numbers in arrays in php?

In php, how do I get the total number of posts (16)?
array1("posts" => 2, "reactions" => 0)
array2("posts" => 4, "reactions" => 4)
array3("posts" => 7, "reactions" => 0)
array4("posts" => 3, "reactions" => 1)
You can use this function
array_merge_recursive
And I made an example for you
<?php
$a = array("posts" => 2, "reactions" => 0);
$b = array("posts" => 4, "reactions" => 4);
$c = array("posts" => 7, "reactions" => 0);
$d = array("posts" => 3, "reactions" => 1);
$array = array_merge_recursive($a,$b,$c,$d);
$sum = array_sum($array['posts']);
echo $sum;
?>
If you want to read more about array_merge_recursive
Follow this Link
Don't know how your variables are named, but some sort of:
$total = 0;
foreach ($arrayofstuff as $item) {
$total += $item['posts'];
}
$numposts = $arr1["posts"]+$arr2["posts"]+$arr3["posts"]+$arr4["posts"]
Do you have them in separate arrays or in one single array? If you have them separately then this would suffice:
$array1 = array("posts" => 2, "reactions" => 0);
$array2 = array("posts" => 4, "reactions" => 4);
$array3 = array("posts" => 7, "reactions" => 0);
$array4 = array("posts" => 3, "reactions" => 1);
$sum = $array1['posts'] + $array2['posts'] + $array3['posts'] + $array4['posts'];
echo $sum;
Outputs:
16

Categories