PHP Multidimensional Array Sorting with Data and Time - php

I am working with any multidimensional array Sorting when I need to sort the array with values and the values should be sorted according tot he date and time. How could I do that.
For Some situation I cant do that through Query I need to Sort through PHP Sort but the predefined function dosent help me.
My Array to be sorted:
Array
(
[events] => Array
(
[0] => Array
(
[0] => 213532
[1] => 100% View
[2] => 12/11/2014 09:00
[3] => 12/11/2014 11:00
)
[1] => Array
(
[0] => 213533
[1] => test 80
[2] => 12/11/2014 06:00
[3] => 12/11/2014 08:00
)
[2] => Array
(
[0] => 213534
[1] => wf4
[2] => 12/12/2014 00:00
[3] => 12/12/2014 23:59
)
[3] => Array
(
[0] => 213535
[1] => Standaard:
[2] => 12/12/2014 10:00
[3] => 12/12/2014 12:00
)
)
)
Every Sub Array of [2] Value.
['events'][$i][2]
Should be sorted and rearranged. in the following way.
12/11/2014 06:00
12/11/2014 09:00
12/12/2014 00:00
12/12/2014 10:00
How can I do that any logical help will be useful.

#Mario is right that usort is what you probably want. You can also use strtotime to convert the string date to an integer, which is easy to compare. I wrote up a working example here. You'll see that I first print out the data (not sorted), then call usort with my custom sort function. When I print out the array again, it's now in order by date.
function compare($a, $b) {
$timeA = strtotime($a[2]);
$timeB = strtotime($b[2]);
if ($timeA < $timeB) {
return -1;
}
if ($timeA > $timeB) {
return 1;
}
return 0;
}
function printArrayContents($arr) {
for ($i = 0; $i < count($arr); $i++) {
echo $arr[$i][2] , "<BR>";
}
}
$events = array(
0 => array(
0 => 213532,
1 => '100% View',
2 => '12/11/2014 09:00',
3 => '12/11/2014 11:00'
),
1 => array(
0 => 213533,
1 => 'test 80',
2 => '12/11/2014 06:00',
3 => '12/11/2014 08:00'
),
2 => array(
0 => 213534,
1 => 'wf4',
2 => '12/12/2014 00:00',
3 => '12/12/2014 23:59'
),
3 => array(
0 => 213535,
1 => 'Standaard:',
2 => '12/12/2014 10:00',
3 => '12/12/2014 12:00'
)
);
echo "Unsorted:<BR>";
printArrayContents($events);
if (usort($events, "compare")) {
echo "Sorted:<BR>";
printArrayContents($events);
} else {
echo "Sort failed.<BR>";
}
Output:
Unsorted:
12/11/2014 09:00
12/11/2014 06:00
12/12/2014 00:00
12/12/2014 10:00
Sorted:
12/11/2014 06:00
12/11/2014 09:00
12/12/2014 00:00
12/12/2014 10:00

What you're looking for is usort(), which allows you to define your own callback/comparison function.
Essentially you'll want something like this:
function my_event_sorter($left, $right) {
if ($left[2] == $right[2])
return 0;
return strtotime($left[2]) < strtotime($right[2]) ? -1 : 1;
}
usort($events, 'my_event_sorter');
This comparison function will receive two parameters (the two elements to be compared) and it's supposed to return one of three integer values:
-1: The left value is smaller than the right one (i.e. order is fine).
0: Both values are identical (i.e. order is fine as well).
1: The right value is smaller than the left one (i.e. order has to be swapped).

if they are unique, you can use the datetime values as a key and then use ksort:
$newArray = array();
foreach($events as $event){
$newArray[$event[2]] = $event;
}
ksort($newArray);

Related

Filter multidimensional array and push in new one

I am trying to build a class schedule that takes a .csv with data for the whole semester. I have this multidimensional array, which contains the date, class, instructor, room, and notes for every day of the semester:
Array (
Array (
[0] => 1
[1] => 2019-04-02
[2] => Study Skills
[3] => 371
[4] => Mr Teacher
[5] => 0
[6] =>
)
Array ( ... )
...)
I managed to display the schedule for the whole semester in one table. However, ideally, I would filter out the data for each month and then push in into a new multidimensional array for the whole semester.
I was able to filter out individual months by comparing it a to hardcoded string but I obviously don't want to do that for the whole semester:
$may = "05";
$classMay = [];
foreach ($days as $day){
if (strrpos($day[1], $may, -5)){
array_push($classMay, $day);
}
};
Is there a more efficient solution to this?
Any insight is much appreciated!
Just get the month name from the date of each one and append the day data onto an array indexed by the month:
foreach($days as $day) {
$month = date('F', strtotime($day[1]));
$result[$month][] = $day;
}
Now you should have an array that looks similar to this:
Array
(
[January] => Array
(
[0] => Array
(
[0] => 1
[1] => 2019-01-01
//etc
)
[1] => Array
(
[0] => 3
[1] => 2019-01-02
//etc
)
)
[February] => Array
(
[0] => Array
(
[0] => 10
[1] => 2019-02-01
//etc
)
[1] => Array
(
[0] => 30
[1] => 2019-02-02
//etc
)
)
)
For fun you could loop 12 months and filter on the month:
foreach(range(1, 12) as $m) {
$month = date('F', strtotime($m));
$result[$month] = array_filter($days, function($v) use($month) {
return $month == date('F', strtotime($v[1]));
});
}

How to convert multi-dimensional array of PHP data to an HTML table?

I'm trying to display a formatted HTML timetable using PHP.
I would like to output data from a multi-dimensional array to an HTML table that's formatted with a total of 8 columns (a column for each day Mon-Sun plus a column on the left showing the start times for each session).
The amount of rows depends on how many sessions there are for each day.
My solution works to a degree and you can see the output in the following image but you can also see that an extra row is generated for some reason. It's always just one extra row no matter the amount of sessions in a day.
The data is represented like this:
Array
(
[0] => Array
(
[0] => Array
(
[id] => 1
[name] => A Monday Session
[start_time] => 10:00
)
[1] => Array
(
[id] => 5
[name] => Another Monday Session
[start_time] => 11:00
)
[2] => Array
(
[id] => 6
[name] => Yet Another Monday Session
[start_time] => 12:00
)
)
[1] => Array
(
[0] => Array
(
)
)
[2] => Array
(
[0] => Array
(
[id] => 8
[name] => A Wednesday Session
[start_time] => 14:30
)
)
[3] => Array
(
[0] => Array
(
[id] => 3
[name] => A Thursday Session
[start_time] => 09:00
)
)
[4] => Array
(
[0] => Array
(
)
)
[5] => Array
(
[0] => Array
(
)
)
[6] => Array
(
[0] => Array
(
[id] => 4
[name] => A Sunday Session
[start_time] => 13:00
)
)
)
As you can see the main keys represent the days of the week. 0 = Monday, 1 = Tuesday etc.
Each day then has a list of sessions. In this example Monday has 3 sessions, and Wed,Thu,Sun each also have one.
Notice that they all have different starting times. The moment a session is introduced to the data with a duplicate start time, then extra rows are also created instead of sharing the same row. See output with the Thursday session changed to 10:00 to be the same as Monday:
And here is my buggy solution. I've marked with a comment the line where I'm going wrong.
// $sessionRows is the array of arrays containing the data above.
// Grabs array of session times from $sessionRows that has been sorted and duplicates removed.
// Current values: Array ( [0] => 09:00 [1] => 10:00 [2] => 11:00 [3] => 12:00 [4] => 13:00 [5] => 14:30 )
$sessionTimes = $this->getSessionTimes($days);
$numOfRows = count($sessionTimes);
$numOfCols = $dayIdx;
// Create grid with correct dimensions and rows represented by the session times
$grid = array();
for($i=0;$i<count($sessionTimes);$i++) {
$row = array();
for($j=0;$j<$numOfCols;$j++) {
$row[] = '<td></td>';
}
$grid[$sessionTimes[$i]] = $row;
}
// Populate grid with session info added to correct coordinates.
for($i=0;$i<$numOfCols;$i++) {
echo count($sessionRows[$i]);
for($j=0;$j<count($sessionRows);$j++) {
$grid[$sessionRows[$i][$j]['start_time']][$i] = $sessionRows[$i][$j];
}
}
$rows='';
$idx = 0;
foreach($grid as $rowArray) {
$rows .= '<tr>';
/*** This lines is the problem! It adds the session time as the first column. ***/
$rows .= '<td class="time-col">'.$sessionTimes[$idx].'</td>';
for($i=0;$i<count($rowArray);$i++) {
if(!empty($rowArray[$i]['name'])){
$rows .= '<td>'.$rowArray[$i]['name'].'<br>'.$rowArray[$i]['start_time'].'</td>';
} else {
$rows .= '<td> - </td>';
}
}
$rows .= '</tr>';
$idx++;
}
return $rows;
The problem was the $sessionTimes array was having duplicates removed by using the array_unique() function.
This function was preserving the index of the removed values. So the extra rows were being created without value.
Changing this to array_values(array_unique($array)) allowed regenerated keys and fixed the issue.
Thanks to anyone who had a look at it, wish I'd realised this earlier!
Here is an alternative solution that uses less loops.
What it does is it loops through the times, then loops through each day to determine whether the session start_time matches, if it does, add it to the output table.
<?php
$sessionTimes = ['09:00', '10:00', '11:00', '12:00', '13:00', '14:30'];
$schedule = [
[
[
'id' => 1,
'name' => 'A Monday Session',
'start_time' => '10:00'
],
[
'id' => 5,
'name' => 'Another Monday Session',
'start_time' => '11:00'
],
[
'id' => 6,
'name' => 'Yet Another Monday Session',
'start_time' => '12:00'
],
],
[
[]
],
[
[
'id' => 8,
'name' => 'A Wednesday Session',
'start_time' => '14:30'
]
],
[
[
'id' => 3,
'name' => 'A Thursday Session',
'start_time' => '09:00'
]
],
[
[]
],
[
[]
],
[
[
'id' => 4,
'name' => 'A Sunday Session',
'start_time' => '13:00'
]
]
];
$output = '<table><thead><tr><th></th><th>Monday</th><th>Tuesday</th><th>Wednesday</th><th>Thursday</th><th>Friday</th><th>Saturday</th><th>Sunday</th></thead><tbody>';
foreach ($sessionTimes as $sessionTime) {
$output .= '<tr><td>'.$sessionTime.'</td>';
$day = 0;
for ($i = 0; $i < count($schedule); $i++) {
if ($i == $day) {
$sessionAtTime = '<td>-</td>';
foreach ($schedule[$day] as $session) {
if (!empty($session) && $session['start_time'] == $sessionTime) {
$sessionAtTime = '<td><b>' . $session['name'] . '</b><br>Start: ' . $sessionTime.'</td>';
}
}
$output .= $sessionAtTime;
$day++;
}
}
$output .= '</tr>';
}
$output .= '</tbody></table>';
echo $output;

PHP join two multidimensional arrays

I have two arrays and I would like to join them. Both arrays are a product of a foreach loop. The first one being:
$cleanNums[] = array(
'01'=>$numbers[1],
'02'=>$numbers[2],
'03'=>$numbers[3],
'04'=>$numbers[4],
'05'=>$numbers[5],
);
and the second one being:
$newDates[] = array(
'day'=>$cleanDate[1],
'month'=>$cleanDate[2],
'year'=>$cleanDate[3],
'draw'=>$cleanDate[6],
);
Using array_merge $weeklyValues = array_merge($newDates,$cleanNums); I'm getting:
Array
(
[0] => Array
(
[day] => 1st
[month] => March
[year] => 2017
[draw] => 660
)
[1] => Array
(
[01] => 3
[02] => 23
[03] => 40
[04] => 20
[05] => 28
)
)
I would like my output to read as follows:
Array
(
[0] => Array
(
[day] => 1st
[month] => March
[year] => 2017
[draw] => 660
[01] => 3
[02] => 23
[03] => 40
[04] => 20
[05] => 28
)
)
Please use this code:
$resultArray = array(
0 => current($newDates) + current($cleanNums)
);
print_r($resultArray);
the array_merge is the way to do it http://php.net/manual/en/function.array-merge.php. The reason for incorrect output is in the way you define variables e.g.
$cleanNums[] = array(...)
Will result in nested array:
array(1) {
[0]=>
array(5) {
...
}
}
To avoid it either change the way of the assignment:
$cleanNums = array(...)
Or the way how you provide it as array_merge parameters:
$weeklyValues = array_merge($newDates[0],$cleanNums[0]);
The same needs to be applied for $newDates of course.
I'm assuming $cleanNums and $newDates are multidimensional arrays and will get more [] values;
$weeklyValues = array();
foreach($cleanNums as $array)
{
$weeklyValues = array_merge($weeklyValues,$array);
}
foreach($newDates as $array)
{
$weeklyValues = array_merge($weeklyValues,$array);
}
print_r($weeklyValues);
$count = count($newDates);
$mergedArray = array();
for($i=0; $i < $count; $i++){
//assuming both arrays have equal number of records
$mergedArray[] = current($newDates[$i]) + current($cleanNums[$i]);
}
var_dump($mergedArray);

Sorting 3 dimensional array at 2nd level based on 3rd level values

I am using the Google Calendar API to pull data from multiple calendars. I am creating an array so I can format the display of the data. I am having trouble sorting the data so events will appear in the proper order.
My primary sort is on datetime ASC. If the two datetimes are equal I want to sort on alldayflag DESC. I only want it sorted within each date.
Here is a sample of my data:
Array
(
[2016-01-29] => Array
(
[0] => Array
(
[date] => January 29
[time] => 8:30 am
[datetime] => 2016-01-29T08:30:00-06:00
[alldayflag] => 0
)
[1] => Array
(
[date] => January 29
[time] => 12:00 am
[datetime] => 2016-01-29T00:00:00-06:00
[alldayflag] => 1
)
[2] => Array
(
[date] => January 29
[time] => 2:00 pm
[datetime] => 2016-01-29T14:00:00-06:00
[alldayflag] => 0
)
[3] => Array
(
[date] => January 29
[time] => 10:00 am
[datetime] => 2016-01-29T10:00:00-06:00
[alldayflag] => 0
)
[4] => Array
(
[date] => January 29
[time] => 12:00 pm
[datetime] => 2016-01-29T12:00:00-06:00
[alldayflag] => 0
)
)
[2016-01-30] => Array
(
[0] => Array
(
[date] => January 30
[time] => 4:00 pm
[datetime] => 2016-01-30T16:00:00-06:00
[alldayflag] => 0
)
[1] => Array
(
[date] => January 30
[time] => 5:00 pm
[datetime] => 2016-01-30T17:00:00-06:00
[alldayflag] => 0
)
[2] => Array
(
[date] => January 30
[time] => 11:00 am
[datetime] => 2016-01-30T11:00:00-06:00
[alldayflag] => 0
)
)
)
I have tried using array_multisort(). I get the proper sorting results however I also get the error message:
Warning: array_multisort(): Array sizes are inconsistent in sort-array.php on line XXX
$getBeginDate = '2016-01-29';
$getEndDate = '2016-01-31';
$getCurrentDate = $getBeginDate;
while(strtotime($getCurrentDate) < strtotime($getEndDate)) {
foreach ($list[$getCurrentDate] as $key => $row){
$datetime[$key] = $row['datetime'];
$alldayflag[$key] = $row['alldayflag'];
}
array_multisort($datetime, SORT_ASC, $alldayflag, SORT_DESC, $list[$getCurrentDate]);
$getCurrentDate = date('Y-m-d', strtotime($getCurrentDate . " +1 day"));
}
I have also tried uasort(). It doesn't sort properly at all.
uasort($list, 'sortCriteria');
function sortCriteria($array, $key) {
if(strtotime($a['datetime']) == strtotime($b['datetime'])) {
if($a['allday'] < $b['allday']) {
return -1;
} else {
return 0;
}
}
return (strtotime($a['datetime']) < strtotime($b['datetime'])) ? -1 : 1;
}
Any help is greatly appreciated.
array_multisort is definitely the way that you want to go. I have an untested answer for you, so may I be smited by the wrath of the community if it's an incorrect guess, but it looks like you're running into a closure issue, or rather the lack thereof. This should fix your issue:
...
while(strtotime($getCurrentDate) < strtotime($getEndDate)) {
$datetime = [];
$alldayflag = [];
foreach ($list[$getCurrentDate] as $key => $row){
...
My guess from the error you mentioned is that if you dumped $datetime before the multisort it would have 5 items in it the first time and 5 items in it the second time.
first_dump => '29th', '29th', '29th', '29th', '29th'
second_dump => '30th', '30th', '30th', '29th', '29th'
Or something similar to this. What's happening is $datetime is persisting through the first round of the while loop into the second time. Which is bombing out your multisort the second time because there are only 3 items and not 5.
I hope that makes sense.
A possible solution could be to use usort.
You can then use a DateTime to compare the 'datetime'.
Then if two datetimes are equal, you can compare the 'alldayflag'.
For example:
function sortCriteria($a, $b)
{
$aDateTime = new DateTime($a['datetime']);
$bDateTime = new DateTime($b['datetime']);
if ($aDateTime == $bDateTime) {
return ($a['alldayflag'] > $b['alldayflag']) ? -1 : 1;
}
return ($aDateTime < $bDateTime) ? -1 : 1;
}
$getBeginDate = '2016-01-29';
$getEndDate = '2016-01-31';
$getCurrentDate = $getBeginDate;
while(strtotime($getCurrentDate) < strtotime($getEndDate)) {
usort($list[$getCurrentDate], 'sortCriteria');
$getCurrentDate = date('Y-m-d', strtotime($getCurrentDate . " +1 day"));
}
Demo

Check PHP array for consecutive indexes

Sorry if the title of the question is unclear, I couldn't sum it up more precisely.
This is the issue:
To begin with, I have an array in this format:
Array (
[0] => 09:00
[1] => 10:00
[2] => 11:00
[3] => 12:00
[4] => 13:00
[5] => 14:00
[6] => 15:00
[7] => 16:00
[8] => 17:00
[9] => 18:00
)
Then some of the members are unset, so after that we're left with something like:
Array (
[0] => 09:00
[1] => 10:00
[6] => 15:00
[7] => 16:00
[8] => 17:00
[9] => 18:00
)
As you see, the array represents time slots. Now, what I need to do is eliminate all time slots shorter than 3 hours. So I need to go through the array and, wherever there are less than 3 members of the original array present, take them out too. So in the example above, since 09:00 and 10:00 are not followed by 11:00, I need to take them out and be left with:
Array (
[6] => 15:00
[7] => 16:00
[8] => 17:00
[9] => 18:00
)
How do I accomplish this? Logically, I think it might be easiest to check for 3 consecutive indexes, rather then checking the actual times but I'm open to any suggestions.
I've solved the problem on my own, and I made it generic so it would work for any duration, not just 3 hours.
$dur=3; //could be anything
foreach($work_times as $member){
$key=array_search($member,$work_times);
$a_ok=0;
for($options=0;$options<$dur;$options++){
$thisone=1;
for($try=$key-$options;$try<$key-$options+$dur;$try++){
if(!array_key_exists($try,$work_times))
$thisone=0;
}
if($thisone==1)
$a_ok=1;
}
if($a_ok==0)
unset($work_times[$key]);
}
$arr = array(
0 => '09:00',
1 => '10:00',
6 => '15:00',
7 => '16:00',
8 => '17:00',
9 => '18:00'
);
// for some testing
$arr += array(12 => '19:00', 13 => '20:00', 14 => '21:00');
$arr += array(16 => '22:00', 17 => '23:00');
$dontRemove = array();
foreach($arr as $key => $val) {
// if the next 2 keys are set
if (isset($arr[$key+1]) && isset($arr[$key+2])) {
$dontRemove[] = $key;
$dontRemove[] = $key+1;
$dontRemove[] = $key+2;
}
}
// combine and diff the keys to get the keys which should be actually removed
$remove = array_diff(array_keys($arr), array_unique($dontRemove));
foreach($remove as $key) {
unset($arr[$key]);
}
print_r($arr);
Try this:
<?php
function check() {
global $array;
$tmpArr = array_keys( $array );
$val1 = $tmpArr[0];
$val2 = $tmpArr[1];
$val3 = $tmpArr[2];
if( ( ++$val1 == $val2 ) && ( ++$val2 == $val3 ) ) {
// continuous
} else {
// not continuous, remove it
unset( $array[$tmpArr[0]] );
}
}
$array = array(
'0' => '09:00',
'1'=> '10:00',
'6' => '15:00',
'7'=> '16:00',
'8' => '17:00',
'9' => '18:00'
);
$total = count( $array );
$ctotal = 0;
while( $ctotal < $total ) {
if( count( $array ) <= 2 ) {
// this array has 2 elements left, which obviously
// nullifies the 3 continuous element check
$array = array();
break;
} else {
//check the array backwards
check();
$total--;
}
}
?>
Hope this helps
$a = Array(
0 => "09:00",
1 => "10:00",
6 => "15:00",
7 => "16:00",
8 => "17:00",
9 => "18:00",
11 => "20:00",
);
foreach ($a as $k => $v) {
// previous or next two time slots exist
$consecutive = (isset($a[$k-1]) or
(isset($a[$k+1]) and isset($a[$k+2])));
if (!$consecutive)
unset($a[$k]);
}

Categories