PHP: Get Value in array without using a loop - php

In my application I have duplicated code include foreach. I have a foreach where I only store the value from $schedules to new array. Then I used another foreach to check if this array is in new $array.
I have an array here $schedules:
$schedules = [
[
"time_start" => "08:00:00",
"time_end" => "12:00:00",
"in_threshold" => 50,
"out_threshold" => 15
],
[
"time_start" => "13:00:00",
"time_end" => "17:00:00",
"in_threshold" => 30,
"out_threshold" => 45
]
];
So I have this code snippet using forloop to store to new array:
foreach($schedules as $schedule){
// Set time_start and time_end to array
$sched_starts[] = ( $schedule[ 'time_start' ] );
$sched_ends[] = ( $schedule[ 'time_end' ] );
// Set time_start and time_end with threshold to array
$time_start[] = date( "H:i:s",
strtotime('+'. $schedule[ 'in_threshold' ] .' minutes',
strtotime ( $schedule[ 'time_start' ]
)));
$time_end[] = date( "H:i:s",
strtotime('+'. $schedule[ 'out_threshold' ] .' minutes',
strtotime( $schedule[ 'time_end' ]
)));
}
I want to get $sched_starts,$sched_ends,$time_start, and $time_end without using a loop to avoid duplicated loops. So I used array_column and array_walk_recursive function but I can't get the $time_start and $time_end then I realized the two functions only get single column in the $schedules at a time.
Is it possible not to use the loop in getting the $time_start and $time_end?

You can use end , which will point to the last element
$c = end($schedules);
$time_start = $c['time_start'];
$time_end = $c['time_end'];

Related

Correct way to make cycle from startdate to enddate in php

I have data of property availability in such format
[
"propertyAvailability" => [
"startDate" => "2022-12-04T00:00:00+00:00",
"endDate" => "2024-12-04T00:00:00+00:00",
"availability" => "NNYNNYYYNNYNYYNYYNYYYYYNNYNNYNNNNYYYYYYYNNNYYYYNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN"
]
]
Where N and Y - statuses of each date from startDate to endDate. And i want to save not available dates in more usable array, to use $data[Y][m] in my code, to receive massive of dates in this month, that not available for booking.
<?php
$raw_data = [
"propertyAvailability" => [
"startDate" => "2022-12-04T00:00:00+00:00",
"endDate" => "2024-12-04T00:00:00+00:00",
"availability" => "NNYNNYYYNNYNYYNYYNYYYYYNNYNNYNNNNYYYYYYYNNNYYYYNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN"
]
];
$results = [];
$i = 0;
$sd = new DateTime($raw_data['propertyAvailability']['startDate']);
$ed = new DateTime($raw_data['propertyAvailability']['endDate']);
$a = str_split($raw_data['propertyAvailability']['availability']);
while ($sd < $ed) {
if ($a[$i] != "Y") {
$t = $sd;
$results[$t->format('Y')][$t->format('m')][] = $t->format('d');
}
$sd->modify('+1 day');
$i++;
}
print_r($results);
?>
Is this correct and effective way to process initial data?

PHP returning an element from an array

So I have some code that returns all the time in an array like this Open hours today: 9:00- 9:45, 9:55 - 10:20, 10:30 - 11:00 . If we used $formatted_ranges[array_key_first($formatted_ranges)] instead of join, it would return a single element as like this, "Open hours today: 9:00 - 9:45". However we need to return like this,
Open hours today: 9:00 - 11:00.
$start = DateTime::createFromFormat( 'H:i', $range['from'] );
$end = DateTime::createFromFormat( 'H:i', $range['to'] );
$formatted_ranges = array_map( function( $range ) {
return $this->format_time( $range['from'] ).' - '.$this->format_time($range['to'] );
}, $ranges );
return sprintf(
__( 'Open hours today:', 'example' ) . ' <span>%s</span>',
join( ', ', $formatted_ranges )
);
If I understand the input data correctly, you don't need to iterate and reformat every element in the multidimensional array.
Just access the from from the first row and the to from the last row and you're done. Format just those two values if necessary.
Code: (Demo)
$ranges = [
['from' => '9:00', 'to' => '9:45'],
['from' => '9:55', 'to' => '10:20'],
['from' => '10:30', 'to' => '11:00'],
];
if (!isset($ranges[0]['from'], $ranges[0]['to'])) {
throw new Exception('insufficient business hours data');
}
printf(
'Open hours today: %s - %s',
$ranges[0]['from'],
$ranges[array_key_last($ranges)]['to']
);
Output:
Open hours today: 9:00 - 11:00
The originally shared code was not runnable. From the question, I think you want to reformat a time range array to find the beginning and the end of all the ranges.
As long as all the time are represented as 24-hour clock format, this is an example of how it can be done.
<?php
/**
* Convert multiple ranges into a single range.
*
* #param array $ranges
* #return array
*/
function overallRanges(array $ranges): array {
if (sizeof($ranges) === 0) {
throw new \Exception('The provided ranges array is empty');
}
$range = array_reduce($ranges, function ($carry, $current) {
$from = DateTime::createFromFormat('H:i', $current['from']);
$to = DateTime::createFromFormat('H:i', $current['to']);
$carry['from'] = ($from < $carry['from']) ? $from : $carry['from'];
$carry['to'] = ($to > $carry['to']) ? $to : $carry['to'];
return $carry;
},
[
'from' => DateTime::createFromFormat('H:i', $ranges[0]['from']),
'to' => DateTime::createFromFormat('H:i', $ranges[0]['to']),
]
);
return [
'from' => $range['from']->format('G:i'),
'to' => $range['to']->format('G:i'),
];
}
// example use
$ranges = [
['from' => '9:00', 'to' => '9:45'],
['from' => '9:55', 'to' => '10:20'],
['from' => '10:30', 'to' => '11:00'],
];
var_dump(overallRanges($ranges));
The output:
array(2) {
["from"]=>
string(5) "9:00"
["to"]=>
string(5) "11:00"
}
Should be a good enough start for you to reformat into anything.

How To Remove Duplicate Elements from array after merging with another array in php?

I am trying to Write Program for Calculating Next 20 dates After Specifying Start date, then from 20 dates i have Exclude Weekends & Holidays(Array holidays('2016-12-13',2016-12-24)) And Result Array which includes only Working Days Excluding Saturday & Sunday, from this Result Array after Passing Holiday array(Eg:- holidays('2016-12-13',2016-12-24))), it must be Excluded from result array. i:e;
I want Expected Output Below mentioned
.
<?php
$Date=array('2016-12-01');
echo "\n <br />Start Date:-" . $Date[0] . "";
/*Code For Generating Next 20 Dates Starts*/
//$start = strtotime($s_row['schedule_start_date']);
$start = strtotime('2016-12-01');
$dates=array();
for($i = 0; $i<20; $i++)
{
array_push($dates,date('Y-m-d', strtotime("+$i day", $start)));
}
echo "\n <br /> Array Of next 20 Days/dates of Given:-";
print_r($dates);
$start=array();
$start=$dates; /*Code For Generating Next 20 Dates Ends*/
$result=array();
$start = strtotime(array_values($Date)[0]);
//$end = strtotime(array_values($Date)[30]);
$result = array();
$begin = new DateTime( '2016-12-01' );
$end = new DateTime( '' );
//$end = $end->modify( '+1 day' );
$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval ,$end);
foreach($daterange as $date)
{
//echo $date->format("Y-m-d") . "<br>";
if (date('N', $start) <= 5) /* 'N' number days 1 (mon) to 7 (sun) */
/*5 weekday */
{
$current = date('Y-m-d', $start); //m/d/Y
$result[$current] = '';
}
$start += 86400;
//echo "Days Without Sat Sun".$result[date($date->format("Y-m-d"))];
//echo "Days Without Sat Sun".$result2[date($current->format("Y-m-d"))];
}
echo " \n <br /> Dates Without Weekends LIKE (Excluding Saturday & Sunday):-";
print_r($result);
/*For Holiday*/
$FinalArray = array();
$holidays = array(
'2016-12-13',
'2016-12-24',
);
echo " \n <br /> Given Holiday Dates Are:-";
print_r($holidays);
$a1 = $result;
$a2 = $holidays;
$array = array_diff(array_merge($a1,$a2),array_intersect($a1,$a2));
echo "\n <br /> Output:-";
print_r($array);
?>
it Gives Output as :- Array ( [2016-12-01] => [2016-12-02] => [2016-12-05] => [2016-12-06] => [2016-12-07] => [2016-12-08] => [2016-12-09] => [2016-12-12] => [2016-12-13] => [2016-12-14] => [2016-12-15] => [2016-12-16] => [2016-12-19] => [2016-12-20] => [2016-12-21] => [2016-12-22] => [2016-12-23] => [0] => 2016-12-13 [1] => 2016-12-24 )
> But I Want Expected Output:-
Array ( [2016-12-01] => [2016-12-02] => [2016-12-05] => [2016-12-06] => [2016-12-07] => [2016-12-08] => [2016-12-09] => [2016-12-12] => [2016-12-14] => [2016-12-15] => [2016-12-16] => [2016-12-19] => [2016-12-20] => [2016-12-21] => [2016-12-22] => [2016-12-23]
You Can Notice That 2016-12-13 is Not There in Above Expected Output as in '2016-12-13', 2016-12-24 is passed as Holiday via holiday array ($holidays = array( '2016-12-13', '2016-12-24', );) i:e; if i pass any date through holidays array it should not be included in result Array(). i:e 2016-12-13 is Available in Result array as well as holiday array So While while printing Final OUTPUT:- 13th date(2016-12-13) Should not be Included in final Output. Anybody Solve this will be Appreciated Thanks in Advance.
When I have to remove duplicates from a array the function that I keep going back to is
array array_unique ( array $array [, int $sort_flags = SORT_STRING ] )
you can find the documentation Here
<?php
$input = array("a" => "green", "red", "b" => "green", "blue", "red");
$result = array_unique($input);
print_r($result);
?>
the output
Array
(
[a] => green
[0] => red
[1] => blue
)
I hope that this was able to help
I prefer to calculate all dates just in one pass. (You may skip filling $dates and $dates_mon_fri arrays if they doesn't used in output also.) There is yet another approach to avoid array_diff() and array_unique() functions. I've used an array_flip() to exchange keys with values in $holdidays array to use fast array_key_exists() function.
<?php
$start = strtotime('2016-12-01');
$holidays = [
'2016-12-13',
'2016-12-24',
];
$dates = [];
$dates_mon_fri = [];
$dates_working = [];
$flip_holidays = array_flip($holidays);
for ($i = 0; $i < 20; $i++) {
$timestamp = strtotime("+$i day", $start);
$date = date('Y-m-d', $timestamp);
$dates[] = $date;
$mon_fri = false;
if (date('N', $timestamp) <= 5) {
$dates_mon_fri[] = $date;
$mon_fri = true;
}
if ($mon_fri && !array_key_exists($date, $flip_holidays)) {
$dates_working[] = $date;
}
}
var_dump($dates);
var_dump($dates_mon_fri);
var_dump($dates_working);
You can avoid using explicit looping:
$begin = new DateTimeImmutable('2016-12-01');
$end = $begin->modify('+20 days');
$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval, $end);
$allDates = iterator_to_array($daterange);
$datesExcludingWeekends = array_filter($allDates, function ($date) {
return (int) $date->format("N") < 6;
});
$datesExcludingWeekends = array_map(
'date_format',
$datesExcludingWeekends,
array_fill(1, count($datesExcludingWeekends), 'Y-m-d')
);
$holidays = [
'2016-12-13',
'2016-12-24',
];
$datesExcludingWeekendsIncludingHolidays = array_flip(array_merge(
$datesExcludingWeekends,
array_diff($holidays, $datesExcludingWeekends)
));
Here is working demo.
Also, take a look at the Carbon library. If you need some exhaustive working with dates this library can really ease your life.

How to get distinct months from array containing dates

I have an array with dates. ie.
array(
0 => '2016-08-01',
1 => '2016-07-15',
2 => '2016-07-01'
);
I need to get the distinct months in array. ie.
array(
0 => 7,
1 => 8
);
I need it to do a foreach to show: June - Juy - August with the distinct months from the dates array. (That part I know how)
You can reformat the date using a combination of array_map, DateTime, and array_unique to achieve that result.
$arr = array(
0 => '2016-08-01',
1 => '2016-07-15',
2 => '2016-07-01'
);
$dates = array_unique(array_map(function($date) {
return DateTime::createFromFormat('Y-m-d', $date)->format('n');
}, $arr));
var_dump($dates);
array(2) {
[0]=>
string(1) "8"
[1]=>
string(1) "7"
}
Of course, it's important to note this results in two dates like 2015-08-11 and 2016-08-04 both showing up as one value in the array. So it's not entirely clear why you would want to do this, but this will meet your specified requirements.
This is the loop you need :
<?php
$arr = array( '2016-08-01',
'2016-07-15',
'2016-07-01' );
$months = array(); // EMPTY ARRAY FOR MONTHS.
foreach ( $arr as $date ) // VISIT EACH DATE IN ARRAY.
{ $mon = substr( $date,5,2 ); // EXTRACT THE MONTH DIGITS.
if ( ! in_array( $mon,$months ) ) // IF MONTH IS NOT IN ARRAY
array_push( $months,$mon ); // INSERT THE MONTH DIGITS.
}
var_dump( $months );
?>
Edit : display month name :
<?php
$arr = array( '2016-08-01',
'2016-07-15',
'2016-07-01' );
$months = array(); // EMPTY ARRAY FOR MONTHS.
foreach ( $arr as $date ) // VISIT EACH DATE IN ARRAY.
{ $mon = substr( $date,5,2 ); // EXTRACT THE MONTH DIGITS.
if ( ! in_array( $mon,$months ) ) // IF MONTH IS NOT IN ARRAY
{ array_push( $months,$mon ); // INSERT THE MONTH DIGITS.
echo date ("F",mktime( null,null,null,$mon,1 ) ); // ◄ MONTH NAME!!!
}
}
?>
Edit #2 : storing month names in array :
<?php
$arr = array( '2016-08-01',
'2016-07-15',
'2016-07-01' );
$months = array(); // EMPTY ARRAY FOR MONTHS.
foreach ( $arr as $date ) // VISIT EACH DATE IN ARRAY.
{ $mon = date("F",mktime( null,null,null,substr( $date,5,2 ),1 ) ); // EXTRACT MONTH.
if ( ! in_array( $mon,$months ) ) // IF MONTH IS NOT IN ARRAY
array_push( $months,$mon ); // INSERT MONTH NAME IN ARRAY.
}
var_dump( $months );
?>
Just use substr on each of your dates array entries to retrieve the chars 5 to 7, then store them in a new array :
$dates = array(
0 => '2016-08-01',
1 => '2016-07-15',
2 => '2016-07-01');
$months = array();
foreach ($dates as $date) {
$months[] = (int)substr($date, 5, 2);
}
$months = array_unique($months); // Remove duplicates
sort($months);

Create zero values for missing items in foreach loop

I have an application that extracts some information from mysql between two dates and returns an associative array. I am producing a graph with this information but have dates missing for the dates in the database that have no information to return. I cannot fix this on the mysql side as I only have read only access to the database.
My database method retrieves an associative array like the below:
[0] => Array
(
[number_of_calls] => 151
[total_call_time] => 00:01:30
[average_call] => 00:02:00
[DATE(calldate)] => 2016-03-18
[direction] => outbound
)
What I am hoping to do is create a daterange from my form like below:
//create data range array
$begin = new DateTime( $datefrom );
$end = new DateTime( $dateto );
$end = $end->modify( '+1 day' );
$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval ,$end);
And then use a foreach loop to iterate through the selected dates of the daterange and pull the value from the associative array where the dates match and if not insert zero values like below:
[number_of_calls] => 0
[total_call_time] => 00:00:00
[average_call] => 00:00:00
I also need the final array to end up in date order. Can anybody help me with this please?
You can transform your $result array to use DATE(calldate) as keys.
$keys = [];
foreach ($result as $item) {
$keys[] = $item['DATE(calldate)'];
}
$result = array_combine($keys, $result);
And your array will look like that:
[2016-03-18] => Array
(
[number_of_calls] => 151
[total_call_time] => 00:01:30
[average_call] => 00:02:00
[DATE(calldate)] => 2016-03-18
[direction] => outbound
)
And you can check if date is presented by simple command:
$key = $datevalue->format('Y-m-d');
if (isset($result[$key])) {
// date exists, use it
} else {
// date not exists, create empty value
}
This is what I ended up doing
// helper function to recursively search array
function recursive_array_search($needle,$haystack) {
foreach($haystack as $key=>$value) {
$current_key=$key;
if($needle===$value OR (is_array($value) && recursive_array_search($needle,$value) !== false)) {
return $current_key;
}
}
return false;
}
foreach ($daterange as $datevalue) {
$key = recursive_array_search($datevalue->format('Y-m-d'), $result);
// if date is found write values from data to output array
if ($key !== False) {
$output_array[] = array("number_of_calls" => $result[$key]['number_of_calls'],
"total_call_time" => $result[$key]['total_call_time'],
"average_call" => $result[$key]['average_call'],
"DATE(calldate)" => $datevalue->format('Y-m-d'),
"direction" => "outbound" );
}
// else write zeros
else {
$output_array[] = array("number_of_calls" => "0",
"total_call_time" => "00:00:00",
"average_call" => "00:00:00",
"DATE(calldate)" => $datevalue->format('Y-m-d'),
"direction" => "outbound" );
}
}
$this->chart_data = $output_array;
}

Categories