I am trying to make a really basic day booking system and need to return all dates within a range, and then remove selected dates from that range. I tried the following code but realised that will remove duplicates which is fine, but I also need that date to be removed too.
Can anyone suggest a good way of doing this?
In the below example I am just hoping to see:
2022-04-03T00:00:00.000000Z
2022-04-04T00:00:00.000000Z
2022-04-05T00:00:00.000000Z
$start_date = "2022-04-01";
$end_date = "2022-04-05";
$datesToRemove = [
'2022-04-01T00:00:00.000000Z',
'2022-04-02T00:00:00.000000Z'
];
$range = Carbon::parse($start_date)->toPeriod($end_date)->toArray();
$available = array_unique(array_merge($range, $datesToRemove));
return $available;
To compare it is necessary to have the compared values in the same format. I decide to morph the $datesToRemove to Carbon format. The you can use to nested loops and check with PHP in_array() function.
$start_date = "2022-04-01";
$end_date = "2022-04-05";
$datesToRemove = [
"2022-04-01T00:00:00.000000Z",
"2022-04-02T00:00:00.000000Z"
];
$range = \Carbon\Carbon::parse($start_date)->toPeriod($end_date)->toArray();
$datesToRemove2 = [];
foreach($datesToRemove as $r) {
$datesToRemove2[] = \Carbon\Carbon::parse($r);
}
$res = [];
foreach($datesToRemove2 as $index => $d1) {
if(in_array($d1, $range)) {
unset($range[$index]);
}
}
return $range;
output
{
"2":"2022-04-03T00:00:00.000000Z",
"3":"2022-04-04T00:00:00.000000Z",
"4":"2022-04-05T00:00:00.000000Z"
}
Means that
Related
I have array like this:
arr=[
627=[
'lead_data'=>['name'=>'Name1', 'date'=>'2019-04-09']
],
500=[
'lead_data'=>['name'=>'Name2', 'date'=>'2018-05-07']
],
534=[
'lead_data'=>['name'=>'Name3', 'date'=>'2019-07-10']
],
100=[
'lead_data'=>['name'=>'Name4', 'date'=>'2019-05-12']
],
]
How can I filter this array where date is between 2019-05-01 and 2019-07-12.
So in result there will be elements with ids 534 and 100.
Or date is >= 2019-07-05 or date is <= 2019-01-01 ?
I know there is array_filter function, but cant understand how to use it in thus case? Please help, thanks
The simplest solution would to just iterate over your data like so:
<?php
$begin = date('Y-m-d', strtotime("2019-05-01"));
$end = date('Y-m-d', strtotime("2019-07-12"));
foreach($array as $key => $data)
{
$date = date('Y-m-d', strtotime($$data['date']));
if (($$data > $begin) && ($date < $end)){
unset($array[$key]);
}
}
var_dump($array);
Always make sure you check out official documentation on php.net because it usually have tons of examples and very thorough explanations.
In your case you can compare dates as strings (since they are in Y-m-d format and comparing them alphabetically will do the trick):
$filtered = array_filter($arr, function ($item) {
return ($item['lead_data']['date'] > '2019-05-01') && ($item['lead_data']['date'] < '2019-07-12');
});
By using array_filter(), and using the use keyword, you can supply variables into the filter - this can be the start- and end-dates for your limits.
When using array_filter(), the data will remain in the array if the return value inside the callback is true, otherwise it will be removed. So then compare the dates, and see if they are greater than $from, and less than $to.
$from = '2019-05-01';
$to = '2019-07-12';
$result = array_filter($arr, function ($v) use ($from, $to) {
return $v['lead_data']['date'] > $from && $v['lead_data']['date'] < $to;
});
print_r($result);
Live demo at https://3v4l.org/Cmt8H
I have several date(strtotime) in a Variable and want the first nearest date that is after the specified date(my date) with php. what do i do?
Variable:
$varD = "1481691600,1482642000,1482037200";
my date:
1481778000 => (2016-12-15)
several date(strtotime):
1481691600 => (2016-12-14)
1482642000 => (2016-12-25)
1482037200 => (2016-12-18) //result
result:
1482037200 => (2016-12-18)
$varD = "1481691600,1482037200,1482642000";
$myDate = "1481778000";
After you explode the string of timestamps ($varD), you can filter them and return the minimum value of the result. Here is one way to do that using array_filter and min.
$comp = function($x) use ($myDate) { return $x > $myDate; };
$firstDateAfterYours = min(array_filter(explode(',', $varD), $comp));
But if you already know that the timestamps in the string will be in ascending order, it will probably be faster not to convert the whole thing to an array and sort through it. You can use strtok to go through it piece by piece and just stop as soon as you get to a timestamp larger than your target.
$ts = strtok($varD, ',');
while ($ts !== false) {
$ts = strtok(',');
if ($ts > $myDate) break;
}
$firstDateAfterYours = $ts;
After pulling in some data from a mysql database saving it to a variable, I'm wondering if it's possible to "query" the variable instead of doing another request to the database? I realise I need to search an array of objects based on key and value. So here is an example of what I have.
<?php
[{"customer":1,"item":1,"bought_at":"2016-12-15 11:41:11"},
{"customer":2,"item":1,"bought_at":"2016-12-15 11:43:21"},
{"customer":3,"item":1,"bought_at":"2016-12-16 13:31:11"},
{"customer":1,"item":2,"bought_at":"2016-12-16 12:12:21"},
{"customer":1,"item":3,"bought_at":"2016-12-17 15:13:58"}]
?>
So lets say I need to search it based on the item number and the date (but not time) when the item was bought. The next step would be to return the result as another array of objects. So if I were to search for item 1 bought at 2016-12-15 it would return.
[{"customer":1,"item":1,"bought_at":"2016-12-15 11:41:11"},
{"customer":2,"item":1,"bought_at":"2016-12-15 11:41:21"},]
Is this possible? If so how would I go about doing it?
Regards
EDIT: The reason I originally asked this question was because I had a query inside a nested foreach loop which bothered me. It's a piece of code that builds up a a json table at the back-end to pass information to the front end to draw a google line graph. Also I changed the data slightly in my original question to try to make it easier to read. It's also built in Laravel. The complete code is pretty large so I'm just posting the nested foreach loops. The query is in the second loop and is given the variable $activations.
foreach ($timeRange as $time){
$temp = array();
$timeTwentyFour = date("G", strtotime($time));
$temp[] = array('v' => "Date(01,01,2000,$timeTwentyFour)");
foreach($data as $row){
$count = 0;
$activations = DB::table('customer_display')->where('display_id',$row->id)->where(DB::raw('DATE(created_at)'),$day)->get();
foreach($activations as $activation){
$timestamp = $activation->created_at;
$activationTime = explode(" ", $timestamp)[1];
if (strtotime($activationTime) >= strtotime($time) && strtotime($activationTime) < strtotime($time) + 3600){
$count++;
};
}
$temp[] = array('v' => (float) $count);
//The custom tooltip
$temp[] = array('v' => $time . ' ' . $row->location . '. ' . $count . ($count == 1 ? ' Activation' : ' Activations'));
}
$rows[] = array('c' => $temp);
}
If those are objects in an array and you only wanted the entries where item is 1 you could use array_filter;
$filtered = array_filter($items, function($item){
// only return objects where this is true
return $item->item == 1;
});
If you wanted only items purchased on the 15th use
return date('d', strtotime($item->bought_at)) == 15;
and if you want to see items 1 bought on the 15th you'd use
$filtered = array_filter($items, function($item){
return $item->item === 1
&& date('d', strtotime($item->bought_at)) == 15;
});
Also check out this answer on comparing dates for more information on how to better do that.
Another database request will be the better approach in most cases. A database is optimized for querying data. It can use indexes, etc. Well known databases like MySQL have a query optimalisation. Doing it by hand will be less efficient.
First downloading too much data and then use something like array_filter to linearly search through all the data is far less efficient than just querying the data with the search criteria in the query.
One way to do it is:
//Prepare statement once
$statement = $pdo->prepare("SELECT * FROM table WHERE item = ? AND bought_at = ?");
$statement->execute(array(1, "2016-12-15"));
foreach ($statement->fetchAll() as $array)
//Do something with $array
//reuse prepared statement with another selection criteria
$statement->execute(array(3, "2016-12-16"));
foreach ($statement->fetchAll() as $array)
//Do something with $array
I need to add a zero to the date value that is posted as the MySQL date value will be used as a directory path to show added photos. Currently the directory is saved as 2012-2-5 and the MySQL entry is 2012-02-05.
I have tried the following, however it doesn't seem to be working:
$dates = array($_POST['photos_year'], $_POST['photos_month'], $_POST['photos_day']);
foreach($dates as $date) {
if (strlen($date) == 1) {
$date = '0' . $date;
}
}
$photos->date = $dates[0] . "-" . $dates[1] . "-" . $dates[2];
I am using a for loop to create the date form, it might be easier for me to edit this however I haven't had any success:
<select name="photos_month" id="photos_month" tabindex="3">
<option value ="">- Month -</option>
<?php
for($date=01;$date<=12;$date++) {
if (isset($photos) && $date==$month) {
echo "<option value='".$date."' selected='selected'>".$date."</option>";
} else {
echo "<option value='".$date."'>".$date."</option>";
}
}
?>
</select>
You need to update the foreach to be
foreach ($dates as &$date)
Currently, $date is a new symbol and will not overwrite the value. That should fix it, but it may be nice to have the correct strings in the options too. You can use str_pad to do that.
When doing a foreach, the element variable $date is a new variable, a copy of the value from the array. You should use a key and update the original array.
foreach($dates as $key => $date) {
if (strlen($date) == 1) {
$dates[$key] = '0' . $date;
}
}
Also, you're running that code on the year, you don't want to be adding a zero to the year, only the month and day.
You could make it a little simpler by just using sprintf with a format string;
$photos_year = '2012';
$photos_month = '2';
$photos_day = '5';
$result = sprintf("%4d-%02d-%02d", $photos_year, $photos_month, $photos_day);
// $result = '2012-02-05'
Check out the string pad function http://php.net/manual/en/function.str-pad.php
Along with the array map function http://php.net/manual/en/function.array-map.php
Using these two together would allow for you to go through each part of the date(array_map) and pad the value with a 0 (using str_pad)
Code would look something like this
$dates = array_map("padString",$dates);
function padString($string) {
return str_pad($string,2,"0",STR_PAD_LEFT);
}
print_r($dates);
//Gives you Array ( [0] => 2012 [1] => 02 [2] => 05 )
use sprintf()
For example:
$date = sprintf('%02d', $_POST['photos_day']);
I'm using a series of MySQL queries to pull back calculations stored by date for graphing via the Flot library. After the calculations are done, the echoed material looks like this (using UNIX timestamp dates):
Item 1:
[
[1159765200000,-117.875],
[1159851600000,-117.25],
[1159938000000,-120.625],
[1160024400000,-122.125],
[1160110800000,-118.125],
[1160370000000,-121.125],
[1160456400000,-123.375],
[1160542800000,-115.625],
[1160629200000,-117.75],
[1160715600000,-112.75],
[1160974800000,-125.25],
[1161061200000,-135],
[1161147600000,-138.375],
[1161234000000,-137],
[1161320400000,-136.25],
[1161579600000,-139.875],
[1161666000000,-146.625],
[1161752400000,-143.625],
[1161838800000,-150.25],
[1161925200000,-152.875],
[1162188000000,-151.75],
[1162274400000,-149.75]
]
Item 2:
[
[1104732000000,47.3913043478],
[1104818400000,45.5072463768],
[1104904800000,45.5797101449],
[1104991200000,45.115942029],
[1105077600000,44.1739130435],
[1105336800000,44.5362318841],
[1105423200000,45.9565217391],
[1105509600000,45.9420289855],
[1105596000000,46.0289855072],
[1105682400000,46.4347826087],
[1106028000000,48.347826087],
[1106114400000,46.8695652174],
[1106200800000,46.4927536232],
[1106287200000,45.6376811594],
[1106546400000,44.3768115942],
[1106632800000,44.0579710145],
[1106719200000,44.5942028986],
[1106805600000,45.0289855072],
[1106892000000,45.231884058],
[1107151200000,46.1449275362],
[1107237600000,46.5942028986],
[1107324000000,45.5652173913],
[1107410400000,45],
[1107496800000,46.2608695652],
[1107756000000,45.7391304348],
[1107842400000,46.3333333333]
]
Basically I'd like to calculate the average of the second value in each pair, controlling for the date. In other words, for each date that matches in each array, print the date and the average of all the second values in each array, e.g:
[Common Date, Average of all second values]
I've looked through a number of array merging techniques but can't seem to find a workable solution.
Thanks very much for any help.
You could construct an array indexed by date in which you put a list of all values for the date:
$byDate = array();
foreach($item1 as $row) {
$date = sprintf('%.0f', $row[0]);
$byDate[$date][] = $row[1];
}
foreach($item2 as $row) {
$date = sprintf('%.0f', $row[0]);
$byDate[$date][] = $row[1];
}
Then you can easily compute the average for each list:
foreach($byDate as $date => $values) {
$avg = array_sum($values) / count($value);
printf("avg for %s: %f\n", $date, $avg);
}
Or compute all averages at once:
function array_avg($array) {
return array_sum($array) / count($array);
}
$avgByDate = array_map('array_avg', $byDate);
Try it here: http://codepad.org/1S1HrYoB
For your merge
$merged_array = array();
function merge_by_time()
{
$passed_arrays = func_get_args();
$merged_array = array();
foreach($passed_arrays as $array)
foreach($array as $value_set){
$merged_array[$value_set[0]][] = $value_set[1];
}
}
return $merged_array;
}
Usage:
$new_array = merge_by_time($array1, $array2, $array3, ...)
Then you'll have an array based on timestamp that has all associated data values contained in it. I think you can take it from here to get the averages?
Second approach
function merge_by_time_and_get_average()
{
$passed_arrays = func_get_args();
$merged_array = array();
foreach($passed_arrays as $array)
foreach($array as $value_set){
$merged_array[$value_set[0]]['data'][] = $value_set[1];
$merged_array[$value_set[0]]['average'] = 0;
foreach($merged_array[$value_set[0]]['data'] as $data_point){
$merged_array[$value_set[0]]['average'] += $data_point;
}
$merged_array[$value_set[0]]['average'] = $merged_array[$value_set[0]]['average']/count($merged_array[$value_set[0]]['data'])
}
}
return $merged_array;
}
Then you have $array[{timestamp}]['data'] containing your data points and $array[{timestamp}]['average'] containing your average of all data points. The nested foreachs are a little messy and expensive, but you can handle it all in one function call.