I have arrays and data from a databse, and need to compare them.
$mydays = array(
'Monday' => array('10:00','11:00','12:00','15:00','16:00','17:00','18:00'),
'Tuesday' => array('10:00','11:00','12:00','15:00','16:00','17:00','18:00'),
'Wednesday' => array('9:00','10:00','11:00','12:00','14:00','15:00','16:00','17:00','18:00'),
'Thursday' => array('9:00','10:00','11:00','12:00','14:00','15:00','16:00','17:00','18:00'),
'Friday' => array('9:00','10:00','11:00','12:00','14:00','15:00','16:00','17:00','18:00'),
'Saturday' => array('9:00','10:00','11:00','12:00')
);
$myhours = array(
"2017-02-27" => array("17:00"),
"2017-03-01" => array("16:00","17:00","18:00"),
"2017-03-03" => array("17:00"),
"2017-03-08" => array("17:00","18:00"),
"2017-03-10" => array("17:00","18:00")
);
From a database, I retrieve some data such as :
$thisday = Monday
$thisdate = yyyy-mm-dd
$thishour = H:i
$myday = $mydays[$thisday];
foreach ($myday as $hour) { /* loop through hours for this specific day */
// I need to find out if there is a date/hour from "$myhours" that matches my data
// ie: I have database:Monday -> I loop through the hours from $mydays:Monday
// I then have : 10:00,11:00,12:00,15:00,16:00,17:00,18:00
// from there, I need to know if, in "$myhours", I have the same hour for "$thisdate"
}
I have read almost every post here, found and tried using :
function in_multiarray($elem, $array, $field)
{
$top = sizeof($array) - 1;
$bottom = 0;
while($bottom <= $top)
{
if($array[$bottom][$field] == $elem) {
return true;
} else {
if(is_array($array[$bottom][$field])) {
if(in_multiarray_d($elem, ($array[$bottom][$field]))) {
return true; } else { return false; }
} else { return false; }
$bottom++;
}
}
return false;
}
if( !in_multiarray("$thisdate", $myhours, "$hour") ) { echo"good"; } else { echo"not good"; }
My problem : I don't get a result every time (even when some day/hour matches), and when I do, it's not for every hour, I miss some days/hours... I thought I would maybe need to reset the array, but had no better result doing it.
Q: is my approach a correct way to work around my problem ? If yes, what am I doing wrong ? if not, what is you best advice, or what shall I do ?
Simple solution using in_array function:
...
$thisday = 'Monday';
$thisdate = "2017-02-27";
$thishour = '11:00';
// check if we have a valid weekday name and date/time value
if (isset($mydays[$thisday]) && isset($myhours[$thisdate])
&& in_array($thishour, $mydays[$thisday])) {
echo 'good';
} else {
echo "not good";
}
Related
So I'm trying to figure out how to check if two times overlap using PHP and a MySQL database.
This is the function I'm using to check if they are overlapping:
function testRange($start_time1,$end_time1,$start_time2,$end_time2) {
return ($end_time1 < $start_time2) ? false : ($start_time1 > $start_time2) && ($start_time1 > $end_time2) ? false : true;
}
AFAIK that should work fine so I go to check if the times are overlapping and add any that do overlap to an array:
$clashes = [];
$idTracker = [];
foreach ($userBands as $show) {
foreach ($userBands as $show1) {
if(!($show->id == $show1->id)) {
if(strcmp($show->date, $show1->date) == 0 && testRange(strtotime($show->time), strtotime($show->time) + ($show->length*60), strtotime($show1->time), strtotime($show1->time) + ($show1->length*60))) {
array_push($clashes, [
"s1_id" => $show->id,
"s2_id" => $show1->id
]);
}
}
}
}
foreach ($clashes as $clash) {
foreach ($clashes as $clash1) {
//If Clash1->ID1 == Clash2->ID1 then
}
print_r($clash);
echo "<br>";
}
However when I print out the entries of the $clashes array I get this as my output:
which is just wrong completely as this is the contents of the database:
27 and 26 should not be clashing with 25, but they are being tracked into the $clashes array. Why is this happening?
Database Info:
Date is of the Date SQL type, Time is of the Time SQL type and Length is a double(8,2) SQL type
$userBands Info:
The $userBands variable is a Laravel collection of the Show model
First:
You can change the SQL query to get end-time like so:
SELECT *,
ADDTIME(
CONCAT(time,'.0'),
CONCAT(CONCAT(CONCAT( length DIV 60, ':' ), length MOD 60) ,':0.0')
) AS EndTime
FROM `table`;
So now you have a column named EndTime,
So this code can work:
if(strcmp($show->date, $show1->date) == 0 &&
newTestRange(
strtotime($show->time),
strtotime($show->EndTime),
strtotime($show1->time),
strtotime($show1->EndTime)
) ) {
I have no idea why this works:
function newTestRange($start_time1,$end_time1,$start_time2,$end_time2) {
// 12:30:00 13:20:00 12:45:00 13:45:00
// return ($end_time1 < $start_time2) ? false : ($start_time1 > $start_time2) && ($start_time1 > $end_time2) ? false : true;
$ret = false; // its all okay.
if ($start_time1 < $start_time2 ){
if ($end_time1 > $start_time2 ){
$ret = true;echo "\n case1-$start_time1-colide-$start_time2 \n";
}
} else if ($end_time2 > $start_time1){
$ret = true; echo "\n case2-$start_time1-colide-$start_time2 \n";
}
return $ret;
}
but you must consider 4 different scenarios:
So if one event contains another event, you will find it.
Best!
Fixed it by changing the algorithm for comparing the times.
New version:
function testRange($s1, $e1, $s2, $e2) {
return $s1 <= $e2 && $s2 <= $e1;
}
Don't know why this works over the previous one however.
I have a series of object keys:
$this->rules->days->mon = isset($this->recurring_event_data['post_ar'][$this->rules->type]['mon']) ? true : false;
$this->rules->days->tue = isset($this->recurring_event_data['post_ar'][$this->rules->type]['tue']) ? true : false;
$this->rules->days->wed = isset($this->recurring_event_data['post_ar'][$this->rules->type]['wed']) ? true : false;
$this->rules->days->thu = isset($this->recurring_event_data['post_ar'][$this->rules->type]['thu']) ? true : false;
$this->rules->days->fri = isset($this->recurring_event_data['post_ar'][$this->rules->type]['fri']) ? true : false;
$this->rules->days->sat = isset($this->recurring_event_data['post_ar'][$this->rules->type]['sat']) ? true : false;
$this->rules->days->sun = isset($this->recurring_event_data['post_ar'][$this->rules->type]['sun']) ? true : false;
I have this function:
function calc_next_weekly_interval($ii,$i)
{
global $array;
// we will roll through this->rules->mon,tue,wed,thu,fri,sat,sun. If its true, return the new iii value, if not keep looping through the array until we hit true again.
$cur_day = strtolower(date('D',$ii));
$found_today = false;
// our initial start date
$iii = $ii;
foreach($this->rules->days as $day => $value)
{
if($found_today == true && $value = true)
{
return $iii
}
// need to find the current day first!
if($day == $cur_day)
{
$found_today = true;
}
$iii + SECS_PER_DAY;
}
}
all good. Note I am trying to find the next true day from the current day. Issue is when I do a search using a Sunday as the initial cur_day, obviously the foreach loop will stop before it finds a true match. How can I continuously loop through an array (Or object keys)? Should I put the loop in a new function and keep calling it with a new start date? I don't want to add extra array keys->values as it will affect things later, I have thought about adding to the initial array only in this function (example here, the array is coded for reference, but in my class - it is of course obj keys->values
If you want to loop continually you can use
$infinate = new InfiniteIterator(new ArrayIterator($array));
foreach ( $infinate as $value ) {
// Do your thing
// Remember to break
}
How about
foreach($this->rules->days as $day => $value)
{
if($day == $cur_day)
{
$set = true;
}
$iii + SECS_PER_DAY;
if($found_today == true && $value = true)
{
return $iii
}
// need to find the current day first!
}
1st i have to say that i am not php professional and this is my 1st time to use return().
so here is the code.
i need to return false and the number of minutes left from the function.
if(!checkIpn())
$err[]='you need to wait'.$nresting.'minutes before you send another request';
function checkIpn()
{
$useripe = 0;
if ( isset($_SERVER["REMOTE_ADDR"]) ) { $useripe = $_SERVER["REMOTE_ADDR"] ; }
else if ( isset($_SERVER["HTTP_X_FORWARDED_FOR"]) ) { $useripe = $_SERVER["HTTP_X_FORWARDED_FOR"] ; }
else if ( isset($_SERVER["HTTP_CLIENT_IP"]) ) { $useripe = $_SERVER["HTTP_CLIENT_IP"] ; }
$query = "SELECT * FROM table WHERE ip = '$useripe' AND status = 'pending' ORDER BY id ASC";
$result = mysql_query($query) or die(mysql_error());
$num_rows = mysql_num_rows($result);
if ( $num_rows > 0 )
{
$str_today = date("Y-m-d H:i:s");
$i=1;
while($row = mysql_fetch_assoc($result))
{
$str_start = $row['date'];
$str_start = strtotime($str_start);
$str_end = strtotime($str_today);
$nseconds = $str_end - $str_start;
$nminutes = round($nseconds / 60);
if ( $nminutes > 120 )
{ return true; }
else {
$nresting = 120 - $nminutes;
return false; }
$i++;
}
}
else { return true; }
based on Tadeck answer below.i did it like this:
$result = checkIpn();
if($result[0]==false)
$err[]='you need to wait '.$result[1].' minutes before you send another request';
thank you Tadeck.
If you want to return two different variables using single call, you can use something like:
return array(true, 0);
or
return array(true);
in first case (when returning success) and
return array(false, $minutes);
in second case (returning failure).
Then you can check it that way:
$result = checkIpn();
if ($result[0]) {
echo 'Success.';
} else {
echo 'Failure. Minutes to wait: '.$result[1];
}
I hope this helps.
EDIT:
If I understand you correctly, you return true or the number of minutes > 0 (larger than zero). Thus you can use such return in case of success:
return true;
and this in case of failure:
return $minutes;
Then you will be able to use it in the code in the following way:
$result = checkIpn();
if ($result === true) {
echo 'Success';
} else {
echo 'Failure. Minutes to wait: '.$result;
}
And I would like to advise you to properly document such function, so that no one is surprised when it returns integer instead of boolean ;)
You can return an array instead of booleans, which allows you to return more than one value:
return array('success'=>FALSE, 'nminutes'=>$nminutes);
But as an alternative, you can just return NULL if the function is successful and return the number of minutes if not. Then you don't need TRUE/FALSE at all.
if ($nminutes > 120)
{
return NULL;
}
else
{
return $nminutes;
}
Check the success like so:
if (checkIpn() !== NULL) // you have minutes left
else // no minutes left - your TRUE case.
You could return an array containing as many things as you like.
In php you can return an array and use the list() function to unpack the values.
function myfn() {
return array(true, 120);
}
...
list($retval, $minutes) = myfn();
This is generally pretty bad style though and I would recommend finding a way to accomplish what you need with 1 return value.
I'm trying to loop through a set of records, all of which have a "number" property. I am trying to check if there are 3 consecutive records, e.g 6, 7 and 8.
I think i'm almost there with the code below, have hit the wall though at the last stage - any help would be great!
$nums = array();
while (count($nums <= 3))
{
//run through entries (already in descending order by 'number'
foreach ($entries as $e)
{
//ignore if the number is already in the array, as duplicate numbers may exist
if (in_array($e->number, $num))
continue;
else
{
//store this number in the array
$num[] = $e->number;
}
//here i need to somehow check that the numbers stored are consecutive
}
}
function isConsecutive($array) {
return ((int)max($array)-(int)min($array) == (count($array)-1));
}
You can achieve the same result without looping, too.
If they just have to be consecutive, store a $last, and check to make sure $current == $last + 1.
If you're looking for n numbers that are consecutive, use the same, except also keep a counter of how many ones fulfilled that requirement.
$arr = Array(1,2,3,4,5,6,7,343,6543,234,23432,100,101,102,103,200,201,202,203,204);
for($i=0;$i<sizeof($arr);$i++)
{
if(isset($arr[$i+1]))
if($arr[$i]+1==$arr[$i+1])
{
if(isset($arr[$i+2]))
if($arr[$i]+2==$arr[$i+2])
{
if(isset($arr[$i+3]))
if($arr[$i]+3==$arr[$i+3])
{
echo 'I found it:',$arr[$i],'|',$arr[$i+1],'|',$arr[$i+2],'|',$arr[$i+3],'<br>';
}//if3
}//if 2
}//if 1
}
I haven't investigated it thoroughly, maybe can be improved to work faster!
This will confirm if all items of an array are consecutive either up or down.
You could update to return an array of [$up, $down] or another value instead if you need direction.
function areAllConsecutive($sequence)
{
$up = true;
$down = true;
foreach($sequence as $key => $item)
{
if($key > 0){
if(($item-1) != $prev) $up = false;
if(($item+1) != $prev) $down = false;
}
$prev = $item;
}
return $up || $down;
}
// areAllConsecutive([3,4,5,6]); // true
// areAllConsecutive([3,5,6,7]); // false
// areAllConsecutive([12,11,10,9]); // true
Here's an example that can check this requirement for a list of any size:
class MockNumber
{
public $number;
public function __construct($number)
{
$this->number = $number;
}
static public function IsListConsecutive(array $list)
{
$result = true;
foreach($list as $n)
{
if (isset($n_minus_one) && $n->number !== $n_minus_one->number + 1)
{
$result = false;
break;
}
$n_minus_one = $n;
}
return $result;
}
}
$list_consecutive = array(
new MockNumber(0)
,new MockNumber(1)
,new MockNumber(2)
,new MockNumber(3)
);
$list_not_consecutive = array(
new MockNumber(5)
,new MockNumber(1)
,new MockNumber(3)
,new MockNumber(2)
);
printf("list_consecutive %s consecutive\n", MockNumber::IsListConsecutive($list_consecutive) ? 'is' : 'is not');
// output: list_consecutive is consecutive
printf("list_not_consecutive %s consecutive\n", MockNumber::IsListConsecutive($list_not_consecutive) ? 'is' : 'is not');
// output: list_not_consecutive is not consecutive
If u don't wanna mess with any sorting, picking any of three numbers that are consecutive should give you:
- it either is adjacent to both the other numbers (diff1 = 1, diff2 = -1)
- the only number that is adjacent (diff = +-1) should comply the previous statement.
Test for the first condition. If it fails, test for the second one and under success, you've got your secuence; else the set doesn't comply.
Seems right to me. Hope it helps.
I think you need something like the following function (no need of arrays to store data)
<?php
function seqOfthree($entries) {
// entries has to be sorted descending on $e->number
$sequence = 0;
$lastNumber = 0;
foreach($entries as $e) {
if ($sequence==0 or ($e->number==$lastNumber-1)) {
$sequence--;
} else {
$sequence=1;
}
$lastNumber = $e->number;
if ($sequence ==3) {
// if you need the array of sequence you can obtain it easy
// return $records = range($lastNumber,$lastNumber+2);
return true;
}
}
// there isn't a sequence
return false;
}
function isConsecutive($array, $total_consecutive = 3, $consecutive_count = 1, $offset = 0) {
// if you run out of space, e.g. not enough array values left to full fill the required # of consecutive count
if ( $offset + ($total_consecutive - $consecutive_count ) > count($array) ) {
return false;
}
if ( $array[$offset] + 1 == $array[$offset + 1]) {
$consecutive_count+=1;
if ( $consecutive_count == $total_consecutive ) {
return true;
}
return isConsecutive($array, $total_consecutive, $consecutive_count, $offset+=1 );
} else {
return isConsecutive($array, $total_consecutive, 1, $offset+=1 );
}
}
The following function will return the index of the first of the consecutive elements, and false if none exist:
function findConsecutive(array $numbers)
{
for ($i = 0, $max = count($numbers) - 2; $i < $max; ++$i)
if ($numbers[$i] == $numbers[$i + 1] - 1 && $numbers[$i] == $numbers[$i + 2] - 2)
return $i;
return false;
}
Edit: This seemed to cause some confusion. Like strpos(), this function returns the position of the elements if any such exists. The position may be 0, which can evaluate to false. If you just need to see if they exist, then you can replace return $i; with return true;. You can also easily make it return the actual elements if you need to.
Edit 2: Fixed to actually find consecutive numbers.
here is my code: http://www.pcgage.net/code.zip (sorry, pasting the code caused it to really mess up, even using the code container).
Scroll to line: 160 (to 174) - this is the loop in question. i want to make it so this is the even part, and then some code to make an odd part, so the loop repeats in this order. The reason is that i want to change the content of this loop alternately.
I am not a coder, so the best thing you could do is to post up the new code and i'll add it in where you tell me too, otherwise i'll get lost :)
Hope that makes sense, if not you can check an earlier post about this issue that explains why i need this (after finding out that css alone cannot solve my problem): css/php: how to solve this div float problem / odd even loop in array
this is the loop:
} elseif ( ( $findpost->ID ) != $id ) {
// all other posts except the current post
$serp_list_li[] = '<div class="serial-contain">
<div class=""><h5>' . $findpost->post_title . '</h5></div>
<div class="text-align">' . $findpost->post_excerpt . ' </div>
<div class="date"> ' . mysql2date('M jS, Y', $findpost->post_date) . ' at ' . mysql2date('g:ia', $findpost->post_date) . '</div>
<div class="comments"><b>' . $findpost->comment_count . ' Comments</b></div>
</div>' . "\n";
}
else {
The three ways are
Modulo
for ($i = 0; $i < 10; $i++)
{
if ($i % 2 == 0)
{
echo "even";
}
else
{
echo "odd";
}
}
Flipping boolean value
$even = true;
for ($i = 0; $i < 10; $i++)
{
if ($even)
{
echo "even";
}
else
{
echo "odd";
}
$even = !$even;
}
And mentioned boolean operator
for ($i = 0; $i < 10; $i++)
{
if ($i & 1 == 0)
{
echo "even";
}
else
{
echo "odd";
}
}
The most fastest is boolean operator. But the most robust is flipping method if you have very different numbers (like running through ID numbers and some are missing).
I haven't looked over the code, but if it's using a variable to count the loop number you can do:
for($i=0;$i<$blah;$i++)
if($i&1){
// ODD
}else{
// EVEN
}
EDIT(1):
I looked at the section you are running into, and now I have another problem, I'm unsure how you are judging what should be odd or not, so I propose two answers:
1: odd loop itteration:
/* Populate the post list array */
// Add here:
$oddLoop = false;
foreach ($findposts as $findpost):
//.....
if($oddLoop=!$oddLoop){
// code for odd loop numbers
}else{
// code for even loop numbers
}
2: Odd ID number:
} elseif ( ( $findpost->ID ) != $id ) {
if($findpost->ID & 1){
// ODD
}else{
//EVEN
}
For loops increment by 1:
$state = 'odd';
for (...)
{
$state = ($state == 'even' ? 'odd' : 'even');
echo $state . PHP_EOL;
}
Output:
even
odd
even
odd
...
If you ever delete an article you could be in trouble - your code assumes that ID runs (odd,even,odd,even) etc.
A better idea would be to create a separate iterator object to feed you the necessary values at each step. Here's what I use:
class LoopingPropertyIterator implements Iterator
{
private $startat = 0, $position = 0;
private $propertylist = array(
'boolean' => array(false, true),
'day' => array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'),
'dow' => array('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat')
);
public function __construct($args, $startat = 0)
{
$this->startat = (int)$startat;
$this->position = $this->startat;
foreach ($args as $name => $arr)
$this->__set($name, $arr);
}
public function __get($name)
{
if (!array_key_exists($name, $this->propertylist))
throw new Exception(__METHOD__ . " unknown property $name");
$t =& $this->propertylist[$name];
if (is_array($t))
return $t[$this->position % count($t)];
else
return $t;
}
public function __set($name, $arr)
{
$this->propertylist[$name] = $arr;
}
public function current()
{
return $this->position;
}
public function key()
{
return $this->position;
}
public function next()
{
++$this->position;
}
public function rewind()
{
$this->position = $this->startat;
}
public function valid()
{
return true;
}
}
then your output simplifies to
$iter = new LoopingPropertyIterator( array(
'outerclass' => array('serial-contain-right','serial-contain-left'),
'innerclass' => array('text-align2','text-align')
));
...
elseif ( $findpost->ID != $id ) {
$link = get_permalink($firstpost->ID);
$title = $findpost->post_title;
$datetime = mysql2date('M jS, Y', $findpost->post_date).' at '.mysql2date('g:ia', $findpost->post_date);
$serp_list_li[]=
<<<TEXT
<div class="{$iter.outerclass}">
<div class="title">
<h5>{$title}</h5>
</div>
<div class="{$iter->innerclass}">{$findpost->excerpt}</div>
<div class="date">{$date}</div>
<div class="comments">
<a href="{$link}#comments"> title="{$title}">
<b>{$findpost->comment_count} Comments</b>
</a>
</div>
</div>
TEXT;
$iter->next();
}
Are you sure $findpost->ID contains sequential numbers?
You could replace the if/else with s short ternary statement like this:
$side = empty($side) || $side == 'right' ? 'left' : 'right';
$serp_list_li[] = '<div class="serial-contain-' . $side . '">' // ...the rest
This would add a 'left' side first.
Get EvenArray and OddArray
NSArray *numberArray = [NSArray arrayWithObjects:#1,#2,#3,#4,#6,#8,#10, nil];
for (id object in numberArray)
{
if ([object integerValue] % 2 == 0)
{
[evenArray addObject:object];
}
else
{
[oddArray addObject:object];
}
}