PHP check if variable is smaller than previous item in array - php

I need a way to check if there are events that overlap each other. So I made an array with the start and end hour of every event. It looks like this:
Array
(
[0] => Array
(
[start] => 0930
[end] => 1200
)
[1] => Array
(
[start] => 1000
[end] => 1230
)
[2] => Array
(
[start] => 1300
[end] => 1530
)
)
This is what I've tried to check if there are events that overlap:
if ( $orders->have_posts() ):
while ($orders->have_posts()) : $orders->the_post();
foreach($order as $o){
$start = $o['start'];
$end = $o['end'];
$attractieID = $o['attractie']->ID;
foreach($order as $key => $attractieID){
$slots[] = array('start' => $start, 'end' => $end);
if($start < $end){
//overlap
}else{
//no overlap
}
}
}
endwhile;
endif;
But this will always give true since I am checking the start and end date of the same item in my array.
I need to way to compare the start value of the current array item and the end value of the previous array item
Anyone knows if this is even possible?
Many thanks in advance!

Start looping at index 1, and compare the start time of the current event with the end of the event with index-1.
$count = count($order);
for ($i = 1; $i < $count; $i++) {
if ($order[$i]['start'] < $order[$i-1]['end']) {
// overlap
} else {
// no overlap
}
}
If you want to do this while also copying from $order to slots, you can use a variable to hold the end time from the previous iteration.
$prevEnd = null;
foreach($order as $o){
$start = $o['start'];
$end = $o['end'];
$attractieID = $o['attractie']->ID;
$slots[] = array('start' => $start, 'end' => $end);
if($prevEnd !== null && $start < $prevEnd){
//overlap
}else{
//no overlap
}
$prevEnd = $end;
}
DEMO

Try this code, with a for loop instead of foreach:
for($i=0;$i<count($order);i++){
$slots[] = array('start' => $start, 'end' => $end);
if($order[$i+1]){
if($order[$i]['start'] < $order[$i+1]['end']{
//overklap
}else{
//no overlap
}
}

Just use a normal for loop. And, to be sure everything is right, sort the array before checking it (if you are sure it is gona be sorted, you can skip that part)
$sortedOrders = usort($order, function($a, $b)
{
$aStart = $a['start'];
$bStart = $b['start'];
return ($a < $b) ? -1 : 1;
});
$count = count($sortedOrders);
for($i = 1; $i < $count; $i++)
{
if($sortedOrders[$i - 1]['end'] > $sortedOrders[$i]['start'])
{
// Handle overlap
}
}

Related

How to group dates together in an array, removing any overlapping dates

I have an array which holds start and end dates like the following:
$dates[] = array('start'=> '2015-01-01', 'end'=> '2016-05-01');
$dates[] = array('start'=> '2016-01-01', 'end'=> '2016-09-11');
$dates[] = array('start'=> '2017-01-05', 'end'=> '2018-02-01');
$dates[] = array('start'=> '2017-01-01', 'end'=> '2017-05-05');
I want to merge the dates together and remove overlapping dates to produce a new array of dates without any overlap. The result of the above would be:
[0] => Array
(
[start] => 2015-01-01
[end] => 2016-09-11
)
[1] => Array
(
[start] => 2017-01-01
[end] => 2018-02-01
)
I'm rather stuck on this. Any ideas on how this could be done?
Since the late hour of the night it will probably not be the most elegant code, but you will get the idea:
First you want to sort the array by start date:
usort($dates, function($a, $b) {
return strtotime($a["start"]) - strtotime($b["start"]);
});
Then you want to initialize the result array, and two more variables to hold the last $start and $end times.
Now you iterate the array and only push a new row if the current start date is bigger than the last end date. If so, you also set the last $start date to the current one. The last $end date you always set to the biggest (compare between current and last).
After the iteration you want to add the last row which your $start and $end variables are holding.
$result = [];
$start = null;
$end = null;
function addToResult(&$result, &$start, &$end, &$date = null)
{
if (!$date || !$start || strtotime($date["start"]) > strtotime($end)) {
if ($start && $end) {
$result[] = ["start" => $start, "end" => $end];
}
if ($date) {
$start = $date["start"];
$end = $date["end"];
}
}
$end = $date && strtotime($end) < strtotime($date["end"]) ? $date["end"] : $end;
}
foreach ($dates as $date) {
addToResult($result, $start, $end, $date);
}
addToResult($result, $start, $end);
https://3v4l.org/Ej40b

PHP array multidimensional: foreach starting from determinate level

I have a multidimensional array that looks like the one below:
Array
(
[results] => Array
(
[0] => Array
(
[hotel_code] => 15ad7a
[checkout] => 2018-04-26
[checkin] => 2018-04-24
[destination_code] => 1c6e0
[products] => Array
.... etc ....
)
[1] => Array
(
[hotel_code] => 193c6c
[checkout] => 2018-04-26
[checkin] => 2018-04-24
[destination_code] => 1c6e0
[products] => Array
.... etc ....
)
Wanting to create a simple pagination of the results, taking from the array 15 records at a time, through the following code I can recover the first 15 records.
$i = 0;
foreach($data['results'] as $key=>$val){
$i++;
$selez_hotel_code = '' . $val['hotel_code'] . '';
if($i == 15) break;
}
Now, how can I get the next 15 values from the array?
I tried to start the foreach directly from record 15, setting it as follows
$i = 0;
foreach($data['results'][14]['hotel_code'] as $val){
$i++;
$selez_hotel_code = '' . $val . '';
if($i == 15) break;
}
but it did not work.
You could use for loop, to specify the start and the end:
$start = 15 ;
$length = 15 ;
$max = count($data['results']);
for ($idx = $start; $idx < $start + $length && $idx < $max; $idx++) {
$val = $data['results'][$idx]['hotel_code'] ;
$selez_hotel_code = $val ;
}
Note that I've added a verification to avoid to loop over the maximum of results ($idx < $max).
You can use $_GET to paginate. Just have a for loop that uses this param if set, 0 if not. Something like this:
$page = isset($_GET['page']) ? intval($_GET['page']) : 0;
for ($i = $page; $i < ($page + 15); $i++) {
// $data['results'][$i] . . .
}
And when clicking on "next page" or whatever you're using, set the page param

How to reverse PHP array in partitions

I have a simple array that looks like this:
Array (
[0] => Array (
[id] => 8692
[name] => d
)
[1] => Array (
[id] => 8691
[name] => c
)
[2] => Array (
[id] => 8690
[name] => b
)
[3] => Array (
[id] => 8689
[name] => a
)
[4] => Array (
[id] => 8500
[name] => d
)
[5] => Array (
[id] => 8499
[name] => c
)
[6] => Array (
[id] => 8498
[name] => b
)
[7] => Array (
[id] => 8497
[name] => a
)
)
This array is quite long so I only included the first 4 items to give you an idea.
My problem is that I need the array to be in a format of
a,b,c,d,a,b,c,d
At the moment the format is like:
d,c,b,a,d,c,b,a
By this I mean the ['name'] value which is either a,b,c or d.
So I every 4 items in the array need to be reversed.
I have tried to achieve this but fail every time ending up with lots of for & while loops.
You can do it using array_chunk, array_merge and array_reverse:
$finalArray = array();
$arrays = array_chunk($myArray, 4);
foreach ($arrays as $array) {
$finalArray = array_merge($finalArray, array_reverse($array));
}
All the answers here, while perfectly valid, are pretty much on the order of O(n^2). So I figured I'd give you an O(n / 2), time complexity, solution as an alternative just in case you care about performance. The solution also uses only O(n + n + k) space complexity (in place swap).
Since the requirement is to reverse order of values, I'm ignoring keys and basing the solution on the constraint that the array is always 0-indexed.
To solve this problem, we can generalize the solution as a simple array reverse, which requires a simple O(n/2) operation with in-place swap. We can achieve this simply with two counters, $i starting from the beginning of the array, and $j starting at the end of the array. Thus, we can swap the values at $arr[$i] with that at $arr[$j] and then increment $i and decrement $j, at each step.
function reverseArray(Array $arr) {
for($i = 0, $j = count($arr); $i < $j; $i++, $j--) {
$tmp = $arr[$j];
$arr[$j] = $arr[$i];
$arr[$i] = $tmp;
}
return $arr;
}
Now, to apply the more specific solution of only reverse every group of 4 elements in the array, we just break up the array in partitions of 4 values, and only reverse each of those partitions at a time. Which just expands on the example above of reverseArray() by altering the starting and ending positions of the $i and $j counter to only reverse within each partition.
Thus we arrive the O(n / 2) solution here by just adding another loop for the partition size, and keep the inner loop from the earlier example.
function reverseArrayPartition(Array $arr, $partitionSize = 4) {
$end = count($arr);
// reverse only one partition at a time
for($start = 0; $start < $end; $start += $partitionSize ) {
$from = $start;
$to = $start + $partitionSize - 1;
for($i = $from, $j = $to; $i < $j; $i++, $j--) {
// swap the transposing values
$tmp = $arr[$j];
$arr[$j] = $arr[$i];
$arr[$i] = $tmp;
}
}
return $arr;
}
$arr = [4,3,2,1,4,3,2,1];
var_dump(reverseArrayPartition($arr)); // expected [1,2,3,4,1,2,3,4]
This will work with any array size at any $partitionSize so it's efficient if you're trying to do this on very large arrays.
You can iterate the array with a for and increment with 4 each time and keep the current offset in other variable and use array_slice to get the current slice of array and reverse order using array_reverse
I am thinking of something like this:
$step = 4;
$offset = 0;
$new_arr = []; //Here we create the new array.
for($i=0; $i<count($arr); $i+=$step) {
$part_of_array = array_slice($arr, $offset, $step);
$part_reverse = array_reverse($part_of_array);
$new_arr = array_merge($new_arr, $part_reverse);
$offset += $step;
}
print_r($new_arr); //Here will be the array as you expected.
All the content in the for can be simplify to:
$new_arr = array_merge($new_arr, array_reverse(array_slice($arr, $offset, $step)));
$offset += $step;
Not harcoding every 4, reverse based on char code of name value
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
/**
*/
$in = [
['name'=>'d','id'=>1]
, ['name'=>'c','id'=>12]
, ['name'=>'b','id'=>13]
, ['name'=>'a','id'=>14]
, ['name'=>'d','id'=>15]
, ['name'=>'c','id'=>16]
, ['name'=>'b','id'=>17]
, ['name'=>'a','id'=>18]
];
$last = PHP_INT_MAX;
$toReverse = [];
$out = [];
foreach ($in as $value) {
$p = ord($value['name']);
if ( $p < $last ) {
//echo 'ToReverse',var_export($value,true),"\n";
$toReverse[] = $value;
}
else {
$out = array_merge($out,array_reverse($toReverse));
//echo 'Join',var_export($out,true),"\n";
$toReverse = [$value];
}
$last = $p;
}
$out = array_merge($out,array_reverse($toReverse));
print_r($out);

PHP: Delete all array elements before a specific one

Hi I've got the following array:
$days = array(
1=>"Sunday",
2=>"Monday",
3=>"Tuesday",
4=>"Wednesday",
5=>"Thursday",
6=>"Friday",
7=>"Saturday"
);
Now I want to make a loop that automatically removes all objects before number 4.
I tried this:
$startIndex = 4;
for($i = 1; $days < $startIndex; $i++)
{
unset($days[$i]);
}
But it does not work.
A shorter solution may be given using array_slice():
$days = array(
1=>"Sunday",
2=>"Monday",
3=>"Tuesday",
4=>"Wednesday",
5=>"Thursday",
6=>"Friday",
7=>"Saturday"
);
$startIndex = 4;
$days = array_slice($days, $startIndex-1, NULL, TRUE);
print_r($days);
returns
Array
(
[4] => Wednesday
[5] => Thursday
[6] => Friday
[7] => Saturday
)
Change $days to $i as $i is your index value.
$days = array(
1=>"Sunday",
2=>"Monday",
3=>"Tuesday",
4=>"Wednesday",
5=>"Thursday",
6=>"Friday",
7=>"Saturday"
);
$startIndex = 4;
for($i = 1; $i < $startIndex; $i++)
{
unset($days[$i]);
}
print_r($days);
Your array
$days = array
(
1=>"Sunday",
2=>"Monday",
3=>"Tuesday",
4=>"Wednesday",
5=>"Thursday",
6=>"Friday",
7=>"Saturday"
);
Loop to remove all element before a specified index.
# Number to stop the unset.
$split_number =4;
# Loop through array
for($a=0;$a<sizeof($days);$a++)
{
if($a < $split_number)
# Unset element if condition is true
unset($days[$a]);
}
print_r($days);
Result
Array
(
[4] => Wednesday
[5] => Thursday
[6] => Friday
[7] => Saturday
)
If you wish the indexes to start from 0 again, you can use the array_values

PHP remove similar arrays based on several associations in an associative array

I have this type of associative array.
Array
(
[0] => Array
(
[bookedArea] => Comp Pool
[laneBooked] => 1
[paidBy] => Edmonton Aurora Synchronized Swim Club
[startTime24hr] => 16:00
[finishTime24hr] => 18:00
)
[1] => Array
(
[bookedArea] => Comp Pool
[laneBooked] => 2
[paidBy] => Edmonton Aurora Synchronized Swim Club
[startTime24hr] => 16:00
[finishTime24hr] => 18:00
)
[2] => Array
(
[bookedArea] => Comp Pool
[laneBooked] => 3
[paidBy] => Keyano Swim Club
[startTime24hr] => 16:00
[finishTime24hr] => 18:00
)
)
I would like to make a new array removing similar entries depending on several associations. startTime24hr, finishTime24hr bookedArea & paidBy if all 4 aren't the same then it shouldn't be removed. Plus I would like to capture the first and last lanes in the new associative array.
Array
(
[0] => Array
(
[bookedArea] => Comp Pool
[firstLaneBooked] => 1
[lastLaneBooked] => 2
[paidBy] => Edmonton Aurora Synchronized Swim Club
[startTime24hr] => 16:00
[finishTime24hr] => 18:00
)
[2] => Array
(
[bookedArea] => Comp Pool
[firstLaneBooked] => 3
[lastLaneBooked] => 3
[paidBy] => Keyano Swim Club
[startTime24hr] => 16:00
[finishTime24hr] => 18:00
)
)
I found this which would help me filter with only one association. However I need 3 more.
$copy = $array; // create copy to delete dups from the array you wish to filter from
$newFilteredArray = array(); // new filtered array.
$item1 = 'startTime24hr'; // the association you want to filter with.
for( $i=0; $i<count($array); $i++ ) {
if ( in_array( $array[$i][$item1], $newFilteredArray ) ) {
unset($copy[$i]);
}
else {
$newFilteredArray [] = $array[$i][$item1]
}
}
print_r($copy);
Edit ----
Thank you Alexander for helping me get the iterations correct.
I have been struggling with inserting into my filtered array the start and finish lanes. I have commented out my attempts to make it work. When its uncommented I get: Notice: Undefined index: startTime24hr and it seems like its in some infinite loop.
But if you comment my stuff no Notices. Can anyone help with this?
function array_push_assoc($array, $key, $value){
$array[$key][] = $value;
return $array;
}
$CPfilteredArray = array();
$lanes = 0;
for($i = 0; $i < count($compPoolArray); $i++) {
$add = true;
// $lanesAdded = false;
// $lanes = $lanes + 1;
// $startLane = $compPoolArray[$i]["laneBooked"];
for($j = 0; $j < count($CPfilteredArray); $j++) {
if(($compPoolArray[$i]["startTime24hr"] === $CPfilteredArray[$j]["startTime24hr"])
&& ($compPoolArray[$i]["finishTime24hr"] === $CPfilteredArray[$j]["finishTime24hr"])
&& ($compPoolArray[$i]["bookedArea"] === $CPfilteredArray[$j]["bookedArea"])
&& ($compPoolArray[$i]["paidBy"] === $CPfilteredArray[$j]["paidBy"])) {
$add = false;
// $lanes = $lanes + 1;
// $lanesAdded = True;
}
}
if($add) {
$CPfilteredArray[] = $compPoolArray[$i];
// if ($lanesAdded){
// $lastLane = $startLane + $lanes;
// $CPfilteredArray[$i] = array_push_assoc($CPfilteredArray, 'firstLane', $startLane);
// $CPfilteredArray[$i] = array_push_assoc($CPfilteredArray, 'lastLane', $lastLane);
// }
// else {
// $CPfilteredArray[$i] = array_push_assoc($CPfilteredArray, 'firstLane', $startLane);
// $CPfilteredArray[$i] = array_push_assoc($CPfilteredArray, 'lastLane', $startLane);
// }
}
}
Two loop cycles do the job.
$filteredArray = array();
for($i = 0; $i < count($array); $i++) {
$add = true;
for($j = 0; $j < count($filteredArray); $j++) {
if(($array[$i]["startTime24hr"] === $filteredArray[$j]["startTime24hr"])
&& ($array[$i]["finishTime24hr"] === $filteredArray[$j]["finishTime24hr"])
&& ($array[$i]["bookedArea"] === $filteredArray[$j]["bookedArea"])
&& ($array[$i]["paidBy"] === $filteredArray[$j]["paidBy"])) {
$add = false;
}
}
if($add) $filteredArray[] = $array[$i];
}
The resulting array called $filteredArray contains the filtered elements.
I know it doesn't really answer (not allowed to comment yet...) your question but if you're retrieving data from db, you could use a query to retrive grouped values.
It could look sth like this (pseudo-SQL)
SELECT
bookedArea
,paidBy
,startTime24hr
,finishTime24hr
,MIN(laneBooked)
,MAX(laneBooked)
FROM
reservation -- ??
WHERE
--condition
GROUP BY
bookedArea
,paidBy
,startTime24hr
,finishTime24hr
If you do know what elements you use to merge array value you may also use a hashmap to do the merge on insertion. So once you add an element to the array you create a hash over
$bookings = array();
$hash = md5($arr['bookedArea'] . $arr['paidBy'] . $arr['startTime24hr'] . $arr['finishTime24hr']);
if ( array_key_exists($hash, $bookings) ){
//add just another lane to the booking
}
else{
//create a new booking entry
}
that way your code may be even more efficient.
This may also work: (You can replace the serialize function to only serialize selected elements)
<?php
$arr=array(
0=>array("a"=>1, "b"=>"hello"),
1=>array("a"=>2, "b"=>"hellooo"),
2=>array("a"=>1, "b"=>"hello"),
);
function removeDuplicates( &$arr ){
$index=array();
$result=array();
foreach ($arr as $key=>$val){
$k=serialize($val);
if(!isset($index[$k])){
$index[$k]=$key;
$result[$key]=$val;
}
}
return $result;
}
$result=removeDuplicates($arr);
print_r($arr);
print '<br>';
print_r($result);
?>

Categories