PHP fill up array holes - php

I've got an array with values stored per year, per week, coming from my database. The result might look like this:
Array
(
[2018] => Array
(
[40] => 1
[41] => 1
[47] => 1
[48] => 1
[52] => 1
)
[2019] => Array
(
[1] => 1
[2] => 1
[3] => 1
[4] => 1
)
)
or
Array
(
[2018] => Array
(
[48] => 1
[49] => 1
)
[2019] => Array
(
[3] => 1
[4] => 1
)
)
As you can see there are weeks without data. I would like to fill up the array, so all weeks are there. Taking the last array as demo, the desired result would be:
Array
(
[2018] => Array
(
[40] => 1
[41] => 1
[42] => 0 < added
[43] => 0 < added
[44] => 0 < added
[45] => 0 < added
[46] => 0 < added
[47] => 1
[48] => 1
[49] => 0 < added
[50] => 0 < added
[51] => 0 < added
[52] => 1
)
[2019] => Array
(
[1] => 1
[2] => 1
[3] => 1
[4] => 1
)
)
Array
(
[2018] => Array
(
[48] => 1
[49] => 1
[50] => 0 < added
[51] => 0 < added
[52] => 0 < added
)
[2019] => Array
(
[1] => 1
[2] =>
[3] => 1
[4] => 1
)
)
So, the arrays should:
fill up holes between two (not consecutively) numbers
fill up the year array until the end if there is a next year
fill up the first weeks of a new year if there is a previous year and no data in the first week
How can I achieve this?

I think the most efficient way is to use the union operator. This should do it:
<?php
$data = array(
"2018"=> array(
"40" => 1,
"41" => 1,
"47" => 1,
"48" => 1,
"52" => 1
),
"2019"=> array(
"1" => 1,
"2" => 1,
"6" => 1,
"7" => 1
)
);
foreach($data as $year=>$array){
$keys = array_keys($array);
$min = min($keys); $max = 52;
if(!isset($data[$year+1])){
$max = max($keys);
}
$data[$year] = $data[$year] + array_fill($min,$max-$min+1, 0);
ksort($data[$year]);
}
print_r($data);
?>
Output:
Array
(
[2018] => Array
(
[40] => 1
[41] => 1
[42] => 0
[43] => 0
[44] => 0
[45] => 0
[46] => 0
[47] => 1
[48] => 1
[49] => 0
[50] => 0
[51] => 0
[52] => 1
)
[2019] => Array
(
[1] => 1
[2] => 1
[3] => 0
[4] => 0
[5] => 0
[6] => 1
[7] => 1
)
)

You can use a set of array functions such as array_fill() and array_replace() to achieve this.
See comments for the explanation
$array = [
'2018' => [
'40' => 1,
'41' => 1,
'47' => 1,
'48' => 1,
'52' => 1
],
'2019' => [
'3' => 1,
'4' => 1
],
];
$result = []; // initialize result array
$years = array_keys($array); // Get given years
$first_year = min($years); // Get first year
$last_year = max($years); // Get last year
foreach ($array as $year => $value) { // Loop thru each year array
$weeks = array_keys($value); // Get weeks
$start = 1; // Set first week
$weeknum = 52; // Set number of weeks
if ($year === $first_year) { // If first year, change number of weeks
$start = min($weeks);
$weeknum -= $start - 1; // number of weeks is 52 - the first week number - 1
}
if ($year === $last_year) { // If last year, change number of weeks to last given week
$weeknum = max($weeks); // Weeks will be 1 - last given week
}
$result[$year] = array_replace(array_fill($start, $weeknum, 0), $value);
}
var_dump($result);
The first goal of this is to fill an array base on the year.
If first year, create array with first given week to week 52
If last year, create array with week 1 to last given week
If somewhere in the middle, create array with week 1 to week 52
Then replace with your existing data array with same keys

Related

Years in Array to key, then populate with many dates in that year

So I have an array like this pulled from the database:
Array
(
[0] => stdClass Object
(
[created] => 2012-08-22 00:00:00
)
[1] => stdClass Object
(
[created] => 2012-08-23 00:00:00
)
[2] => stdClass Object
(
[created] => 2012-08-24 00:00:00
)
[3] => stdClass Object
(
[created] => 2012-08-24 00:00:00
)
[4] => stdClass Object
(
[created] => 2012-08-24 00:00:00
)
...
There's currently several thousand records...
From that, I'm trying to build an array like what's below
Array (
[2016] array (
[01] array(), //Jan
[02] array( //Feb
[01] = 8 // number of occurances
[02] = 6 // number of occurances
.... etc
),
... etc
)
[2015] array(
[01] array(), //Jan
[02] array(), //Feb
... etc
)
... etc...
)
I'm not entirely sure how to do this completely dynamically (IE: no hard coding the year keys).
I'm open to other ways to pull the information from the database as well using MySQL instead of PHP/foreach loops on thousands of records.
Not that it matters significantly, I'm using Codeigniter 3 on PHP 5.6 & MySQL 5.6.28 so I tagged those.
Edit: If you haven't guessed, I'm creating graphs...
EDIT
As requested by #WEBjuju below is my working method. During the process, I discovered I also needed totals for the year and each month. I have not yet tested this for speed.
// much of this function provided by Stackoverflow user #Webjuju
// http://stackoverflow.com/posts/41067942
$all_dates = $this->db->select('created')->order_by('created', 'ASC')->get('jobs')->result(); // produces original array above
foreach ($all_dates as $obj)
{
$stt = strtotime($obj->created);
list($year, $month, $day) = explode('-', date('Y-m-d', $stt));
#$target[$year]['total']++;
#$target[$year][$month]['total']++;
// increment the string for each occurrence of that day/month/year
#$target[$year][$month]['counts'][$day]++;
}
$minyear = 2012; // TODO: get this dynamically
$maxyear = date('Y');
for ($y = $minyear; $y <= $maxyear; $y++)
{
for ($m = 1; $m < 13; $m++)
{
$m01 = (strlen($m)<2) ? "0$m" : $m;
$days_in_m = date('t', strtotime("$y-$m-01"));
for ($d = 1; $d <= $days_in_m; $d++)
{
// single digit? add preceeding 0
$d01 = (strlen($d)<2) ? "0$d" : $d;
// if day isn't in array, add it with 0 as a value
if (empty($target[$y][$m01]['counts'][$d01])) $target[$y][$m01]['counts'][$d01] = 0;
// put in correct place but works for now
ksort($target);
ksort($target[$y]);
ksort($target[$y][$m01]);
ksort($target[$y][$m01]['counts']);
}
}
}
$data->trends = $target;
Which now produces an array like this:
Array
(
[2013] => Array
(
[total] => 1510 // total for year
[01] => Array // Jan
(
[counts] => Array
(
[01] => 0 //Jan 1
[02] => 1 // Jan 2
[03] => 1 //...
[04] => 3
[05] => 0
[06] => 0
[07] => 2
[08] => 1
[09] => 2
[10] => 4
[11] => 5
[12] => 0
[13] => 0
[14] => 0
[15] => 5
[16] => 21
[17] => 0
[18] => 1
[19] => 0
[20] => 0
[21] => 0
[22] => 0
[23] => 3
[24] => 2
[25] => 13
[26] => 0
[27] => 0
[28] => 24
[29] => 4
[30] => 3
[31] => 6
)
[total] => 101 // total for Jan
)
...
// repeats for all years found in db
// and all months and days for those years.
I now have working graphs...
A single loop over the $arr pulled from the database will do:
foreach ($arr as $obj) {
$stt = strtotime($obj->created);
list($year, $month, $day) = explode('-', date('Y-m-d', $stt));
// #$target[$year][$month] = array(); // original OP request
// increment the string for each occurrence of that day/month/year
#$target[$year][$month][$day]++;
}
die('<pre>'.print_r($target,true));
Producing
Array
(
[2012] => Array
(
[08] => Array
(
[22] => 1
[23] => 1
[24] => 1
)
[09] => Array
(
[22] => 1
[23] => 1
)
)
[2013] => Array
(
[08] => Array
(
[24] => 2
)
)
)
(from the following test data):
$_01 = new stdClass();
$_01->created = '2012-08-22 00:00:00';
$_02 = new stdClass();
$_02->created = '2012-08-23 00:00:00';
$_03 = new stdClass();
$_03->created = '2012-08-24 00:00:00';
$_04 = new stdClass();
$_04->created = '2012-09-22 00:00:00';
$_05 = new stdClass();
$_05->created = '2012-09-23 00:00:00';
$_06 = new stdClass();
$_06->created = '2013-08-24 00:00:00';
$_07 = new stdClass();
$_07->created = '2013-08-24 00:00:00';
$arr = [$_01, $_02, $_03, $_04, $_05, $_06, $_07];
EDIT - FILL IN THE ZEROS!
for ($y = $minyear; $y <= $maxyear; $y++) {
for ($m = 1; $m < 13; $m++) {
$m01 = (strlen($m)<2) ? "0$m" : $m;
$days_in_m = date('t', strtotime("$y-$m-01"));
for ($d = 1; $d <= $days_in_m; $d++) {
$d01 = (strlen($d)<2) ? "0$d" : $d;
if (empty($target[$y][$m01][$d01])) $target[$y][$m01][$d01] = 0;
}
}
}
THEN THE KEY SORTING
foreach ($target as &$month) {
uksort($month, zeroonesort);
foreach ($month as $day) {
uksort($day, zeroonesort);
}
}
function zeroonesort($a, $b) {
$ai = (integer) $a;
$bi = (integer) $b;
// in php 7 use the spaceship operator <=>
// otherwise do it the old way
switch (true) {
case ($ai == $bi): return 0;
case ($ai > $bi): return 1;
case ($ai < $bi): return -1;
}
}
PRODUCES
Array
(
[2012] => Array
(
[01] => Array
(
[01] => 0
[02] => 0
[03] => 0
[04] => 0
[05] => 0
[06] => 0
[07] => 0
[08] => 0
[09] => 0
[10] => 0
[11] => 0
[12] => 0
[13] => 0
[14] => 0
[15] => 0
[16] => 0
[17] => 0
[18] => 0
[19] => 0
[20] => 0
[21] => 0
[22] => 0
[23] => 0
[24] => 0
[25] => 0
[26] => 0
[27] => 0
[28] => 0
[29] => 0
[30] => 0
[31] => 0
)
[02] => Array
(
[01] => 0
[02] => 0
[03] => 0
[04] => 0
[05] => 0
[06] => 0
[07] => 0
[08] => 0
[09] => 0
[10] => 0
[11] => 0
[12] => 0
[13] => 0
[14] => 0
[15] => 0
[16] => 0
[17] => 0
[18] => 0
[19] => 0
[20] => 0
[21] => 0
[22] => 0
[23] => 0
[24] => 0
[25] => 0
[26] => 0
[27] => 0
[28] => 0
[29] => 0
)
[03] => Array
(
etc...

PHP preg_match constant output

I'm trying to parse out a string that could have multiple values and if one doesn't exist the array is outputting the part I'm looking for up one spot, causing me logic headaches.
What I'm trying to parse: P1DT12H15M, or PT1H5M, or PT15M
Basically it's P(Number of Days) T(Number of Hours)(Number of Minutes). The P and T are constant. Here's the match string I have so far:
'/P([0-9]*?)D?T([1-2]?[0-9]?)H?([1-5]?[0-9]?)M?/'
It pulls everything apart, but the array output is not what I'm looking for.
PT2H gives Array ( [0] => PT2H [1] => [2] => 2 [3] => )
PT2H15M gives Array ( [0] => PT2H15M [1] => [2] => 2 [3] => 15 )
But
PT15M gives Array ( [0] => PT15M [1] => [2] => 15 [3] => )
I need that number to be in position 3, if possible.
$exp = '/P(?:([0-9]+)D)*T(?:([1-2]?[0-9])H)*(?:([1-5]?[0-9])M)*/';
preg_match($exp, 'PT2H15M', $m); print_r($m);
preg_match($exp, 'PT15M', $m); print_r($m);
preg_match($exp, 'P1DT12H15M',$m); print_r($m);
result
Array
(
[0] => PT2H15M
[1] =>
[2] => 2
[3] => 15
)
Array
(
[0] => PT15M
[1] =>
[2] =>
[3] => 15
)
Array
(
[0] => P1DT12H15M
[1] => 1
[2] => 12
[3] => 15
)
So, you can take $P = m[1], $H= m[2], $M = m[3]
Your input strings looks like time interval specifiers. Let the PHP internal class DateInterval parse them then access the properties of [DateInterval] to get the values you need:
$int = new DateInterval('P1DT12H15M');
print_r($int);
produces:
DateInterval Object
(
[y] => 0
[m] => 0
[d] => 1
[h] => 12
[i] => 15
[s] => 0
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
[invert] => 0
[days] =>
[special_type] => 0
[special_amount] => 0
[have_weekday_relative] => 0
[have_special_relative] => 0
)
You can access the properties directly (they are public):
printf("%d days, %d hours, %d minutes\n", $int->d, $int->h, $int->i);
If you need them as array you can even convert $int to an array and use them this way:
$date = (array)$int;
printf("%d days, %d hours, %d minutes\n", $date['d'], $date['h'], $date['i']);
If you insist, you can even emulate the behaviour of the preg_match() you struggle to create:
$string = 'P1DT12H15M';
$interval = new DateInterval($string);
$array = (array)$interval;
$matches = array_merge(array($string), array_slice(array_values($array), 0, 6));
print_r($matches);
displays:
Array
(
[0] => P1DT12H15M
[1] => 0
[2] => 0
[3] => 1
[4] => 12
[5] => 15
[6] => 0
)
(use array_slice(..., 3, 6) to keep only the time components)

how to count common values ina array

Array (
[attendance_id] => 18
[attendance] => 1
[student_id] => 1
[date] => 2015-01-19
[in_time] => 00:00:00
[out_time] => 00:00:00
[fee_amount] => 15000 )
Array (
[attendance_id] => 19
[attendance] => 1
[student_id] => 2
[date] => 2015-01-19
[in_time] => 00:00:00
[out_time] => 00:00:00
[fee_amount] => 2000 )
Array (
[attendance_id] => 20
[attendance] => 0
[student_id] => 1
[date] => 2014-01-15
[in_time] => 00:00:00
[out_time] => 00:00:00
[fee_amount] => 0
)
I want to count the common values in these Arrays. Please guide how to do this.
As result I want to get this:
date [2014-01-15 ] => 2
attendance [1] => 2
This is actually for a small institute where single student details for a day may entered into the system as above. So I just want to make a report day end saying how many students has come to the class on specific day and how many didn't.
Try this..
<?php
$array = array(array('18','2015-01-19','15000'),array('2015-01-19','18','22'),array('20','11','22'));
$newcountarray = array();
foreach ($array as $key=>$value) {
foreach ($value as $newvalue) {
if ($foundKey = array_key_exists($newvalue,$newcountarray)) {
$newcountarray[$newvalue] += 1;
}
else{
$newcountarray[$newvalue] = 1;
}
}}
print_r($newcountarray);
?>
Output
Array ( [18] => 2 [2015-01-19] => 2 [15000] => 1 [22] => 2 [20] => 1 [11] => 1 )
Yes Deena Let's say
$array = array(array('18',<==row id no
'2015-01-19',<=date
'1',<=student id>
'2'<=present(1),absent(2))),
array(array('19',<==row id no
'2015-01-19',<=date
'2',<=student id>
'1'<=present(1),absent(2))),
its like for the day 2015-01-19 i want to get all 2 stuudent and among them 1 Student is present and 1 is absent

2 arrays count how many times id = date

Ive sat with this for a while now, and its getting late.
Im trying to get a top 3 of most sales from last month, and i need to count how many times a id from array 1 is equal to array 2 last month(6 = last atm.) like id 4 = 2, id 7 = 3
It might not be the perfect solution, but im just trying to break it down by my self, so later on 'maybe' problems, will i take care of when i hit the wall,
so please, if anyone can help me here, ill be greatfull.
UPDATE
- I will add the result im looking for here: (sorry i didnt before, it makes it alot easier :-)
- The result below, is because i want the count from 2014-06-01 and up to the last day of that month monly, on array[0][1] under this array, only 6-7-8 is not from 2014-06
Hope it makes a bit more sense now ^^
Array
(
[0] => Array
(
[0] => Array
(
[4] => 2
[7] => 3
[1] => 2
[3] => 2
[9] => 1
[12] => 1
[2] => 1
[13] => 1
)
)
)
Array
(
[0] => Array
(
[0] => Array
(
[0] => 4
[1] => 4
[2] => 7
[3] => 1
[4] => 7
[5] => 7
[6] => 3
[7] => 3
[8] => 4
[9] => 9
[10] => 12
[11] => 2
[12] => 13
[13] => 1
)
[1] => Array
(
[0] => 2014-06-18
[1] => 2014-06-10
[2] => 2014-06-05
[3] => 2014-06-05
[4] => 2014-06-12
[5] => 2014-06-11
[6] => 2013-12-12
[7] => 2014-07-23
[8] => 2014-05-13
[9] => 2014-06-01
[10] => 2014-06-12
[11] => 2014-06-04
[12] => 2014-06-04
[13] => 2014-06-11
)
)
)
I hope that what I understood is what you're really asking for. let's say your array definition is :
$arr = Array
(
0 => Array
(
0 => Array
(
0 => 4,
1 => 4,
2 => 7,
3 => 1,
4 => 7,
5 => 7,
6 => 3,
7 => 3,
8 => 4,
9 => 9,
10 => 12,
11 => 2,
12 => 13,
13 => 1
),
1 => Array
(
0 => "2014-06-18",
1 => "2014-06-10",
2 => "2014-06-05",
3 => "2014-06-05",
4 => "2014-06-12",
5 => "2014-06-11",
6 => "2013-12-12",
7 => "2014-07-23",
8 => "2014-05-13",
9 => "2014-06-01",
10 => "2014-06-12",
11 => "2014-06-04",
12 => "2014-06-04",
13 => "2014-06-11"
)
)
);
If you need to test if the date is lower than 6 month and then put their id, sales and date you need to use this code
$result = [];
$index=0;
foreach ($arr[0][0] as $key => $value)
{
$date1 = new DateTime($arr[0][1][$key]);
$date2 = new DateTime();
$diff = $date1->diff($date2);
$diff = ($diff->format('%y') * 12) + $diff->format('%m');
if($diff<=6)
{
$result[$index]['id'] = $key;
$result[$index]['sales'] = $value;
$result[$index]['date'] = $arr[0][1][$key];
$index++;
}
}
var_dump($result);
array_count_values() will give you the number of times each value appears in array 1.
$count = array_count_values($array[0][0]);
Result:
Array
(
[4] => 3
[7] => 3
[1] => 2
[3] => 2
[9] => 1
[12] => 1
[2] => 1
[13] => 1
)
Then you can use a loop to combine with array 2:
$result = array();
foreach($count as $key=>$val) {
$result[$array[0][1][$key]] = $val;
}
Result:
Array
(
[2014-06-12] => 3
[2014-07-23] => 3
[2014-06-10] => 2
[2014-06-05] => 1
[2014-06-01] => 1
[2014-06-04] => 1
[2014-06-11] => 1
)
See demo

php merging arrays

In the multidimensional array below, I would like to merge arrays that have the same merge_id. I'm not sure "merge" is the right word: in the example below, array['0'] should become array['0'] with in it array['0']['0'] and array['0']['1'], the latter being equal to array['1']. I hope this makes sense ...
The array comes out of the db sorted on merge_id so arrays with matching merge_id are always "next to" each other, and there will only ever be 2 with the same merge_id
As I loop through the array I know I need to keep a variable that is always equal to the previous merge_id and if there is a match between previous and current, then merge.
Array
(
[0] => Array
(
[client_id] => 5
[company_name] => company111_name
[id] => 3
[fee] => 111
[year] => 2009
[quarter] => 3
[date_inserted] => 1264948583
[description] => 2009 - Q3
[fee_type] =>
[merge_id] => a87ff679a2f3e71d9181a67b7542122c
[total_paid] => 0
[total_remainder] => 0
)
[1] => Array
(
[client_id] => 5
[company_name] => company111_name
[id] => 6
[fee] => 55.5
[year] => 2010
[quarter] => 2
[date_inserted] => 1264949470
[description] => 2010 - Q2
[fee_type] =>
[merge_id] => a87ff679a2f3e71d9181a67b7542122c
[total_paid] => 0
[total_remainder] => 0
)
[2] => Array
(
[client_id] => 5
[company_name] => company111_name
[id] => 4
[fee] => 111
[year] => 2009
[quarter] => 4
[date_inserted] => 1264948583
[description] => 2009 - Q4
[fee_type] =>
[merge_id] =>
[total_paid] => 0
[total_remainder] => 0
)
[3] => Array
(
[client_id] => 5
[company_name] => company111_name
[id] => 7
[fee] => 55.5
[year] => 2010
[quarter] => 3
[date_inserted] => 1264949470
[description] => 2010 - Q3
[fee_type] =>
[merge_id] =>
[total_paid] => 0
[total_remainder] => 0
)
)
Code
$merger = $data['search']['0']['merge_id'];
$i = 0;
foreach($data['search'] as $fee)
{
if($fee['merge_id'] == $merger)
{
//bump up & merge arrays
???
}
$merger = $fee['merge_id'];
$i++;
}
You can use the ID as key to put all items with the same ID in the same array:
$merged = array();
foreach ($data['search'] as $fee) {
if ($fee['merge_id'] == '') {
continue;
}
if (!isset($merged[$fee['merge_id']])) {
$merged[$fee['merge_id']] = array();
}
$merged[$fee['merge_id']][] = $fee;
}
$merged = array_values($merged);
Notice that this will skip the items with an empty merge ID. You could also use a default merge ID in that case by replacing continue; with $fee['merge_id'] = 0;.
foreach($array as $p)
$result[$p['merge_id']][] = $p;

Categories