Search Array for Best Match based on Day & Time - php

I'm trying to work out how to the the correct Site base on the current Day and Time from an array. The examples array just shows Monday, the real array will contain 7 days of the week with multiple values for each day.
This is the example array :
$arr = array (
array( 'Day' => 'Monday',
'Start' => '0830',
'End' => '1730',
'Site' => 'NW1'),
array( 'Day' => 'Monday',
'Start' => '1200',
'End' => '1300',
'Site' => 'PL1'),
array( 'Day' => 'Monday',
'Start' =>'1730',
'End' => '2130',
'Site' => 'RE1')
);
So Monday at 1100 I should get NW1, at 1800 I should get RE1, but between 1200-1300 I should get PL1
So far this is the code I have:
$today = 'Monday'; // Full day name
$time = '1205';
echo "<br/>Day: $today";
echo "<br/>Time: $time <br/><br/>";
$arr = array (
array( 'Day' => 'Monday',
'Start' => '0830',
'End' => '1730',
'Site' => 'NW1'),
array( 'Day' => 'Monday',
'Start' => '1200',
'End' => '1300',
'Site' => 'PL1'),
array( 'Day' => 'Monday',
'Start' =>'1730',
'End' => '2130',
'Site' => 'RE1')
);
usort($arr, function($a, $b) {
return (date("N", strtotime($a['Day'])) <=> date("N", strtotime($b['Day']))) * 100 +
($a['Start'] <=> $b['Start']) * 10 +
($a['End'] <=> $b['End']);
});
foreach ($arr as $i => $values) {
if ($today != $values['Day']) continue;
if ($time >= $values['Start'] && $time <= $values['End']) {
$site = $values['Site'];
break;
}
}
echo "$today # $time Site => $site";
This works for times between 0830-1730 & 1730-2130, but not if the time is 1200-1300.
I'm assuming I need to search the array for the Day match, then the Start time and check the end time, but I'm not sure how to do this ?
Can anyone point me in the right direction.
Thanks
**** UPDATE ****
New example array with additional entries
Array
(
[0] => Array
(
[Day] => Monday
[Start] => 0830
[End] => 1730
[Site] => NW1
)
[1] => Array
(
[Day] => Monday
[Start] => 0930
[End] => 0945
[Site] => PK1
)
[2] => Array
(
[Day] => Monday
[Start] => 1200
[End] => 2100
[Site] => PL1
)
[3] => Array
(
[Day] => Monday
[Start] => 1230
[End] => 1245
[Site] => EM1
)
[4] => Array
(
[Day] => Monday
[Start] => 1730
[End] => 2130
[Site] => RE1
)
}
The expected results are:
0940 = PK1
1430 = PL1
0920 = NW1
The aim is 0830 to 1730 NW1 is correct unless something else overrides this, ie 1200-2100 PL1 would be correct, after 2100 to 2130 RE1 etc.
Thanks

All you need to do is reverse the sorting based on start time
usort($arr, function($a, $b) {
return (date("N", strtotime($a['Day'])) <=> date("N", strtotime($b['Day']))) * 100 +
($b['Start'] <=> $a['Start']) * 10 +
($a['End'] <=> $b['End']);
});
This way, we will loop through the array starting with the latest start time (for each day) and once we find a match, we break the loop, using the site value for the match.
I'm still not really sure of the purpose of the multiplication in your usort, but it doesn't seem to be causing any problems, so I'm going to leave it in.
DEMO

As far as I can see as soon as you find a $time between the "start" and "end" you assign the value to $site and then "break" straight away without looking through the rest of the array.
But if the time is 1230 on a Monday which entry in the array should take precedence?
If it is the last one, then maybe hang on to a variable until you search the entire array like so:
$lastCorrectEntry;
foreach ($arr as $i => $values) {
if ($today != $values['Day']) continue;
if ($time >= $values['Start'] && $time <= $values['End']) {
$lastCorrectEntry = $values['Site'];
}
}
$site = $lastCorrectEntry;

Related

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;

How to calculate two consecutive values in array

I am trying to loop through an array and calculate each value with next one. The data is like
[
['rate' => 1000, 'date' => '2017-07-10'],
['rate' => 2000, 'date' => '2017-08-14'],
['rate' => 3000, 'date' => '2017-08-18'],
['rate' => 1000, 'date' => '2017-07-23']
]
I have this [edited, following an example from another question in stackoverflow]:
#foreach ($users as $user)
#if ($user->admin_id == $active_user) // filtering users under this admin
#foreach ($userbillings as $userbilling)
#if($userbilling->user_id == $user->id) // filtering users
<?php
$count = count($userbilling->created_at);
$rates[] = $userbilling->rate;
$diff = 0;
for ($i=0; $i < $count; $i++) {
if (isset($rates[$i + 1])) {
$thisValue = $rates[$i];
$nextValue = $rates[$i + 1];
$diff = $nextValue - $thisValue;
}
}
echo $diff;
?>
#endif
#endforeach
#endif
#endforeach
This gives me result: 1000 2000 3000 1000 3000
What I want to achieve is to subtract first item from second, second item from third, third item from forth and so on, until it reaches to the last item. For last item, I want to keep the whole number.
I understand that I need to use array and loop here, and I have tried different ways for past 2 weeks, but cannot get even close.
Please suggest me how to make it work, or give me a guideline how I should proceed. I have searched plenty of questions here, but could not find anything right for my problem. If there is any, please suggest me the link.
You haven't specified your preferred output but assuming that you want to end up with an array result of the calculations for all pairs, plus the last element as-is, this should get you on the right track.
I would also second a comment above - do not do this sort of logic in a view as it belongs in a controller or somewhere like a service. Keep your views as simple as possible.
Not tested so please do so yourself:
<?php
$periods = [
['rate' => 1000, 'date' => '2017-07-14'],
['rate' => 3000, 'date' => '2017-08-18'],
['rate' => 2000, 'date' => '2017-08-10'],
['rate' => 3000, 'date' => '2017-08-15'],
['rate' => 1000, 'date' => '2017-08-23'],
];
$lastPeriod = count($periods) - 1;
$ratesForPeriods = [];
foreach ($periods as $index => $currentPeriod) {
if ($index === $lastPeriod) {
$ratesForPeriods[] = $currentPeriod; // add the last as-is
} else {
$nextPeriod = $periods[$index + 1];
$currentDate = new DateTime($currentPeriod['date']);
$interval = $currentDate->diff(new DateTime($nextPeriod['date']));
$ratesForPeriods[] = [
'from' => $currentPeriod['date'],
'to' => $nextPeriod['date'],
'rate' => $currentPeriod['rate'],
'days' => $interval->days,
'total' => $interval->days * $currentPeriod['rate'],
];
}
}
print_r($ratesForPeriods);
Yields:
Array
(
[0] => Array
(
[from] => 2017-07-14
[to] => 2017-08-18
[rate] => 1000
[days] => 35
[total] => 35000
)
[1] => Array
(
[from] => 2017-08-18
[to] => 2017-08-10
[rate] => 3000
[days] => 8
[total] => 24000
)
[2] => Array
(
[from] => 2017-08-10
[to] => 2017-08-15
[rate] => 2000
[days] => 5
[total] => 10000
)
[3] => Array
(
[from] => 2017-08-15
[to] => 2017-08-23
[rate] => 3000
[days] => 8
[total] => 24000
)
[4] => Array
(
[rate] => 1000
[date] => 2017-08-23
)
)
Hope this helps :)
Your question is not clear, but here is an example that might help you!
use array_slice() & array_sum()
$array = array( "0"=>1,"1"=>1,"2"=>5,"3"=>1,"4"=>1,"7"=>1,"8"=>3,"9"=>1);
$keys = array_keys($array);
$array = array_values($array);
$newArr = array();
foreach ($array as $key=>$val) {
$newArr[] = array_sum(array_slice($array, 0, $key+1));
}
$newArr = array_combine($keys, $newArr);
print '<pre>';
print_r($newArr);
print '</pre>';
Reference:
array_slice()
array_sum()
array_combine()
array_keys()
You can access the next item in foreach loop like following code :
foreach ($items as $key => $item)
{
echo !empty($items[$key+1]) ? ($items[$key+1]->rate - $item->rate) : $itema->rate;
}

PHP Multidimensional Array Sorting with Data and Time

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);

Selecting a certain item from multi-dimensional array

I have an multi-dimensional array called shifts which contains the day (e.g. Monday) and then the shift (e.g. 12:00 - 16:00)
I'm looking to do something like:
$monday = shift from array where day equals Monday
My array currently looks like:
Array ( [0] => Array ( [day] => Saturday [shift] => Day Off! )
[1] => Array ( [day] => Sunday [shift] => Day Off! )
[2] => Array ( [day] => Monday [shift] => 11:00-19:00 )
[3] => Array ( [day] => Tuesday [shift] => 08:00-17:00 )
[4] => Array ( [day] => Wednesday [shift] => 08:00-17:00 )
[5] => Array ( [day] => Thursday [shift] => 16:00-01:00 )
[6] => Array ( [day] => Friday [shift] => 16:00-01:00 ) )
Array is built using this code:
$shifts = array();
$sql = mysql_query("SELECT day, shift FROM ps_shifts WHERE user_id = '$user_id' AND week_begin = '$week_1_begin'");
while($row = mysql_fetch_assoc($sql)) {
$shifts[] = $day;
}
/* to refine on what was said above lets take the the data as such:*/
$data = array(
array ( 'day' => 'Saturday', 'shift' => 'Day Off!' ),
array ( 'day' => 'Sunday', 'shift' => 'Day Off!' ),
array ( 'day' => 'Monday', 'shift' => '11:00-19:00' ),
/* ... */
);
/* we then take that data and transform it, that is flatten it to day=>shift. call it shifts*/
$shifts = array();
$days = array('Saturday','Sunday','Monday','Tuesday','Wednesday','Thursday', 'Friday');
foreach ($data as $i=>$s) {
$shifts[$days[$i]] = $data[$i]['shift'];
}
/* this returns an array like:
$shifts = array (
'Saturday' => 'Day Off!',
'Sunday' => 'Day Off!',
'Monday' => '11:00-19:00',
);
*/
/* then to get the shift for any day is as simple as.*/
$monday_shift = $shifts['Monday'];
print "monday shift is: $monday_shift\n";
You could use the day as the array key.
So:
while($row = mysql_fetch_assoc($sql)) {
$shifts[$day['day']] = $day;
}
Then you could get it by doing this:
$shift_time = $shifts['Monday']['shift'];
Solved based on 'jd182' anwser, however the provided code didn't work so I modified to this:
$shifts = array();
$sql = mysql_query("SELECT day, shift FROM ps_shifts WHERE user_id = '$user_id' AND week_begin = '$week_1_begin'");
while($row = mysql_fetch_assoc($sql)) {
$shifts[$row['day']] = $row['shift'];
}
echo $shifts['Monday'];

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