PHP Multidimensional Array - php

I have a logical structure or idea of something i want to happen and i wanted it to be coded in PHP using array, here's the scenario:
10 Loops/occurrence of month
month => repetition
jan => 2
mar => 1
sep => 5
dec => 2
As shown above i have a loop that occur 10 times, within that count of loops 4 different month have been recorded, and every specific month there is a chance that is has been repeated multiple times like the month (sep) has been repeated 5times. This idea is same as example a record of a person of what are the months he/she traveled and how many times he traveled within that month from 10 recorded travels. Any idea how to build this in PHP using Arrays? Any suggestions or answers are greatly appreciated!
Ok here's a real deal what i wanted to happen, i pull the records of months from a database, then i wanted to display the months pulled from database and the how many times it has been repeated, example the display are like these:
Jan (2)
Sep (5)
I wanted it to be dynamic, no certain month will be displayed if it doesn't exist from the database record.

I think he's trying to perform rudimentary frequency analysis. Assuming you have dates fed into an array somehow, you can proceed by using strtotime(), date() functions to generate keys, and then parse them.
First, create some dummy data:
// dummy data; unsure of the format, so going with time()-like structure
$dates = array();
for( $i = 0; $i < 10; $i++ ) {
$dates[] = strtotime(sprintf("%s-%s-2012", rand(1,30), rand(1,12) ));
}
Now perform a frequency count, keyed by month written by date("M" ...):
$freq = array();
foreach( $dates as $timestamp ) {
// assuming it's all the same year, discover the month
$month = date("M", $timestamp );
if( !isset($freq[$month]) ) $freq[$month] = 0;
$freq[$month]++;
}
At this point, you'll have something resembling what you want:
print_r( $freq );
Array
(
[Sep] => 2
[Feb] => 2
[Nov] => 1
[Dec] => 1
[Jan] => 2
[Apr] => 1
[May] => 1
)
If the year varies, you may want to double-joint this somehow, or otherwise.

Related

How replace days in php array comparing days in a given array [duplicate]

I have an array which consists of 7 days names. This array will be dynamic everytime. So i want to check which day is missing from an array. For ex,
[Monday,Tuesday,Thursday,Friday,Saturday,Sunday]
Here, the wednesday is missing so output should be wednesday
Sometimes there will be more then one day will be missing and sometimes none, so the output should be and array which will contain all missing days.
You can use array_diff function to get missing days.
$days = ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'];
$inputDays = ['Sunday','Friday'];
$missingDays = array_diff($days,$inputDays);
print_r($missingDays);
Output
Array
(
[0] => Monday
[1] => Tuesday
[2] => Wednesday
[3] => Thursday
[5] => Saturday
)
array_diff is case sensitive, you may need to convert string to lower case.
Demo
Since you tagged both js and php but didn't specify which language you're using for this part of your code, here's a JS solution (which doesn't have a handy array_diff function)
var days = ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"];
var input = ["Sunday","Friday"];
return days.filter(function(day) {return input.indexOf(day) < 0;});
you can compare two arrays with array_diff. Example: https://3v4l.org/4g00a

Check CSV for 2 rows with the same text & time within 60mins

I am looping through a CSV file with the following structure
text,time
"Hey you",20181219T15:59:00
"Hey you",20181219T15:39:00
"Random",20181219T15:39:00
This simply contains a 'text string' and another string that represents an ISO 8601 date.
I want to perform a function that will check if a) it has found two rows with the exact same text & has the timeframe between these is within 60 minutes?
Can anyone suggest the best way to approach this (taking into account the CSV will be a max of 50 entries so want to make this as efficient as possible.
So my array returned looks like this once the dates are parsed via DateTime
Array
(
[0] => Array
(
[text] => Hey you
[time] => DateTime Object
(
[date] => 2018-12-19 15:59:00.000000
[timezone_type] => 3
[timezone] => Europe/London
)
)
[1] => Array
(
[text] => Hey you
[time] => DateTime Object
(
[date] => 2018-12-19 15:39:00.000000
[timezone_type] => 3
[timezone] => Europe/London
)
)
)
What would be the best method to check for the exact same text AND time is within one hour of the same text?
Assuming that the array is ordered by Datetime (so bigger key means newer date time), you can do as follow:
// set an array to track text we meet
$textList = [];
foreach ($array as $key => $element){
$text = $element['text'];
if (!array_key_exists($text, $textList)) {
// first time we meet this text, we track it and its position in the array
$textList[$text] = $key;
}else{
// second time we meet this test, we compare the current date time with the previous one to get difference in minutes
$currentTime = $element['time'];
$previousTimeKey = $textList[$text];
$previousTime = $array[$previousTimeKey]['time'];
$diff = $currentTime->diff($previousTime);
// total minutes of diff: hours*60 + minutes
$diffInMinutes = $diff->format('%h') * 60 + $diff->format('%i');
if ($diffInMinutes < 60) {
// do whatever you need..
}
}
}
Complicate this for your particular needs. ;)
PS if the array is not ordered by date time, consider to order it before and then use this function: the algorithm with an array not ordered by date time would be much harder.
Tried this with your given array
My solution with the time difference might not be pretty but i noticed the other answer doesn't take days/months/years into the time difference so it will pass if the date is different but time of the day is less than 60minutes.
This also worked no matter if the first date is older or younger, so the array sorting isn't needed.
function findMatch($arrays){
$tmp_list = []; //takes the first occurance of text
foreach ($arrays as $key => $array) {
if(isset($tmp_list[$array['text']])){
//possible match check for time difference
$difference = $tmp_list[$array['text']]->diff($array['time']);
if($difference->y === 0 && $difference->m === 0 && $difference->d === 0 && $difference->h === 0 && $difference->i <= 59 && $difference->s <= 59){
//less than hour difference
}else{
//more than hour difference
}
}else{
$tmp_list[$array['text']] = $array['time'];
}
}
}
findMatch($arrays);

How to store days of week using bit field?

I’m re-factoring an application, and one of the database tables stores days of the week in columns. Each day can either be “on” or “off”.
The notion of on and off makes me think this data could be more succinctly represented as bits. But I’ve not worked with raw bits as a data type before.
The application is written in PHP. I’ve seen bits used to store header values here. There are then “helper” constants (HEADER_X_FORWARDED_ALL and HEADER_X_FORWARDED_ALL that act as “pre-configurations” for all headers being used, or a sub-set being used for AWS ELB-like environments (where some headers are not sent, so are “off”).
So I have a couple of questions using this data type for representing days of the weeks:
How would I store the final bit value in MySQL? As in the column data type, length etc.
How would I check a day is “on”? If I have the value 0b0100000, how do I determine using bitwise operators that Tuesday is “on”?
How do I “set” days to persist the value in the database? So if I have checkboxes in the UI that submits the values as an array, how would I convert something like:
Array
(
[days] => Array
(
[Monday] => 0
[Tuesday] => 1
[Wednesday] => 0
[Thursday] => 0
[Friday] => 0
[Saturday] => 0
[Sunday] => 0
)
)
…to a bit value like 0b0100000, again using bitwise operators?
If someone could point me in the right direction of getting started with working with bit values in PHP, I’d be most grateful!
You could store the decimal values of the selected days. So as a really basic example:
/**
* Keys are ISO-8601 numeric representation
* of the days of the week
*/
$days = [
1 => 0b0000001, // Monday => 1
2 => 0b0000010, // Tuesday => 2
3 => 0b0000100, // Wednesday => 4
4 => 0b0001000, // Thursday => 8
5 => 0b0010000, // Friday => 16
6 => 0b0100000, // Saturday => 32
7 => 0b1000000, // Sunday => 64
];
You'd just loop and check if the day's bit is set in the value (bitwise and):
// The value you'd store in your database
$mondayToFriday = 31; // 1 + 2 + 4 + 8 + 16
foreach ($days as $n => $v) {
/**
* Get the day's name using strftime (has locale capability)
* strtotime("Sunday +n days") would work with ISO-8601 day
* numbers or PHP's date("w") (0 - 6 / Sunday - Saturday)
*/
$day = strftime("%A", strtotime("Sunday +{$n} days"));
echo "{$day}: " . (($v & $mondayToFriday) ? "Y" : "N") . PHP_EOL;
}
As for your checkboxes, you could just build the binary string from the state of each day (0 or 1), and convert it to decimal using bindec() (the order in which they appear in the array is important, you'd need to ensure that they're correctly mapped to their corresponding day's value).
Given your example input, and the days in this example, the following would work:
$dec = bindec(strrev(implode("", $input["days"])));
Hopefully that'll push you in the right direction.
Here's an example to play with

PHP: I have an assoc array with time ranges ($k=>$v integers), I want to trim and combine overlapping schedules

Premise: I'm working at a project that needs to be PHP 5.2 compliant, I cannot use PHP 5.3+ DateInterval or other PHP>=5.3-only instructions, regrettably.
I have an array that contains business hours expressed as $k => $v ranges, so the key is a start, the value is an end, like so:
array(
09:00 => 11:30
09:30 => 11:00
10:00 => 12:30
13:30 => 14:30
)
In this example we have the first three pairs of ranges that overlap, I could express the same thing as 09:00 => 12:30 (meaning: opens at 9 am, closes at 12.30 pm), since the start and end of the first three pairs overlap.
I could also write the array like so, as integers (or I could use floats e.g. 09:30 becomes 9.3 it doesn't matter I think):
array(
900 => 1130
930 => 1100
1000 => 1230
1330 => 1430
)
How to turn the array into:
array(
900 => 1230
1330 => 1430
)
Ideas that come to my mind are looping the array, using array_slice, passing values by reference and unset() things while at it... But I'm not sure if that would be the best idea or I'm just overcomplicating it.
There are many solutions to this problem; here's mine:
http://phpfiddle.org/main/code/batv-hzqw
Hopefully it is clear enough and fulfills the criteria that you have laid out -- If not please do tell me :)
One way to do this type of overlap checking is to use this algorithm truth: What's the most efficient way to test two integer ranges for overlap?
Pseudo code
Loop through all of your time ranges and put them into the same bucket if they overlap.
Break those buckets down into single elements with the lowest start and highest end time.
PHP example:
<?php
$times = array(
900 => 1130,
930 => 1100,
1000 => 1230,
1330 => 1430,
845 => 900,
1330 => 1700,
845 => 1000
);
function reduceOverlap($times) {
$reduced = array();
//Put the first entry into our final bucket
$reduced[array_keys($times)[0]] = $times[array_keys($times)[0]];
foreach ($times as $start => $end) {
// Trip this flag if a new bucket does not need to be created
$foundOverlap = false;
// Check if this time can go in one of our buckets
foreach ($reduced as $reducedStart => $reducedEnd) {
// Truthy check for overlap of time range with bucket range
if ($start <= $reducedEnd && $end >= $reducedStart) {
// Use this for start key incase it gets changed
$startKey = $reducedStart;
// Was the start less than the bucket's start?
// If yes update bucket's start time
if ($start < $reducedStart) {
unset($reduced[$reducedStart]);
$reduced[$start] = $reducedEnd;
$startKey = $start;
}
// Was the end greater than the bucket's end?
// If yes update bucket's end time
if ($end > $reducedEnd) {
$reduced[$startKey] = $end;
}
$foundOverlap = true;
}
}
// There was no overlap, create a new bucket
if (!$foundOverlap) {
$reduced[$start] = $end;
}
}
return $reduced;
}
var_dump(reduceOverlap($times));
Output:
array(2) {
[1330]=>
int(1700)
[845]=>
int(1230)
}

Calculating weighted mean for values in an array

I have an array that contains half-hourly time periods; this array is then sub-divided into dates going back 6 weeks of 'today', each date having the amount of transactions completed on that date, in that half hour period. e.g.
[07:30:00] => Array
(
[2015-05-18] => 10
[2015-05-25] => 17
[2015-06-01] => 11
[2015-06-08] => 20
[2015-06-15] => 16
[average] => 15
)
[08:00:00] => Array
(
[2015-05-18] => 12
[2015-05-25] => 10
[2015-06-01] => 14
[2015-06-08] => 19
[2015-06-15] => 18
[average] => 15
)
This goes on for the entire business day.
The average I'm currently calculating above is a simple mean of all those values, calculated thus:
foreach($average as $half_hour => $data) {
$average[$half_hour]['average'] = round(array_sum($data)/count($data), 0, PHP_ROUND_HALF_UP);
}
What I'd like to achieve is applying more weight to the more recent dates, and lesser weight to the older values. I started off defining an array of dates I'd encounter, and setting a weight for that particular date:
$date_weights = array(
date("Y-m-d", strtotime("6 weeks ago $day")) => 0.05,
date("Y-m-d", strtotime("5 weeks ago $day")) => 0.05,
date("Y-m-d", strtotime("4 weeks ago $day")) => 0.15,
date("Y-m-d", strtotime("3 weeks ago $day")) => 0.20,
date("Y-m-d", strtotime("2 weeks ago $day")) => 0.25,
date("Y-m-d", strtotime("1 weeks ago $day")) => 0.30
);
The weights all add up to 1, which from what I understand of weighted mean calculations, keeps things simple(r). Except, this is where my poor-math brain threw an exception and refused to play any more.
I tried the following code, but that appears not to do what I'm looking for:
foreach($average as $half_hour => $data) {
// apply the desired weighting to the values
foreach($data as $date => $transactions) {
$average[$half_hour][$date] = ($date_weights[$date]*$transactions);
}
}
foreach($average as $half_hour => $data) {
$average[$half_hour]['average'] = round(array_sum($data)/count($data), 0, PHP_ROUND_HALF_UP);
}
I think I'm on the right track here ? But I'm obviously missing a step. Can any kind slightly more math-savvy person point out where I'm going wrong and how best to achieve where I'm trying to get with this? Thank you !!
Simply remove the division by count($data) so that you get
$average[$half_hour]['average'] = round(array_sum($data), 0, PHP_ROUND_HALF_UP);
Or pick weights that have an average of (instead of a sum) of one, and keep the division.
Also, if you have 6 weight you need to have 6 values - otherwise the weights don't add up to one. Now you seem to have 6 weights (in $date_weights but only 5 values (in $average[$half_hour], not counting average).
EDIT: If you don't want to worry about the weights adding up to one, you can use this:
$average[$half_hour]['average'] = round(array_sum($data)/array_sum($date_weights), 0, PHP_ROUND_HALF_UP);
You still have to make sure all of the weights are used, though.

Categories