Fill 2d array with rows for missing periods of array - php

I have an array that contains periods from 1 - 13. Sometimes the array doesn't contain data for all periods and I need to fill in the missing ones, for example:
$array = [
['period' => 7, 'y' => 20],
['period' => 8, 'y' => 20.50],
['period' => 9, 'y' => 7020],
['period' => 10, 'y' => 6520],
['period' => 11, 'y' => 65920],
['period' => 12, 'y' => 62820],
['period' => 13, 'y' => 6120],
];
For this case I need to run a php loop to fill in the missing first 6 periods with 0 y values. I've tried a variety of loops but with no joy.
Desired output:
[
['period' => 1, 'y' => 0],
['period' => 2, 'y' => 0],
['period' => 3, 'y' => 0],
['period' => 4, 'y' => 0],
['period' => 5, 'y' => 0],
['period' => 6, 'y' => 0],
['period' => 7, 'y' => 20],
['period' => 8, 'y' => 20.50],
['period' => 9, 'y' => 7020],
['period' => 10, 'y' => 6520],
['period' => 11, 'y' => 65920],
['period' => 12, 'y' => 62820],
['period' => 13, 'y' => 6120],
]

You can get good semantics with using the standard array methods. For example:
<?php
$in = [
['period' => 7, 'y' => 20],
['period' => 8, 'y' => 20.50],
['period' => 9, 'y' => 7020],
['period' => 10, 'y' => 6520],
['period' => 11, 'y' => 65920],
['period' => 12, 'y' => 62820],
['period' => 13, 'y' => 6120],
];
// collect available periods
$available = array_column($in, 'period');
// calculate missing periods
$missing = array_diff(range(1, 13), $available);
// transform missing to correct format
$addition = array_map(function ($period) { return ['period' => $period, 'y' => 0]; }, $missing);
// add missing to input
$out = array_merge($in, $addition);
// sort by period
usort($out, function ($a, $b) {
return $a['period'] <=> $b['period'];
});
// done
print_r($out);
demo: https://3v4l.org/2fDYW

start by filling the whole array you need with all period with a value of 0.
Then for each period you get in your data array, use the period id to update the value of period in the right place in the array.
Hope it helps...

You can try this
for ($i=1 $<=13, $i++) {
$exits = false;
foreach ($array as $key => $value) {
if ($value['period'] == $key) {
$exits = true;
}
}
if ($exits) {
$array[] = ['period' => $i , 'y' => 0];
}
}

This should solve your problem:
Let's say $p_array is your array that contains periods from 1 - 13.
// get all periods number that is not missed
$not_missed_periods = array();
foreach ($p_array as $p_each)
{
$not_missed_periods[] = $p_each['period'];
}
// loop for checking all valid periods i.e 1-13
for ($i=1; $i<=13; $i++)
{
// check if $i OR period is not in $not_missed_periods
if (!in_array($i, $not_missed_periods)) {
$p_array[] = array('period' => $i, 'y' => 0);
}
}
print_r($p_array);

Assuming your $array is sorted by period.
You can create a new array that copies the content or your $array and set a new content for missing periods.
$new_array = [];
for ($i = 1, $j = 0; $i <= 13; $i++) {
if ($array[$j]['period'] == $i) {
$new_array[] = $array[$j]; //copy contents
$j++;
} else {
$new_array[] = [ 'period' => $i, 'y' => 0 ]; // set new contents
}
}

Use array_column() to generate a lookup array from your input array -- effectively applying associative, numeric, first-level keys without disturbing the original row data.
Then iterate from 1 to 13. If the current iteration's integer is found in the lookup, then push the found row; otherwise push the default row containing the incremented value.
Code: (Demo)
$lookup = array_column($data, null, 'period');
$result = [];
for ($i = 1; $i <= 13; ++$i) {
$result[] = $lookup[$i] ?? ['period' => $i, 'y' => 0];
}
var_export($result);

Related

array_diff with calculation

Please, consider the following arrays:
$reference = array(
'080604' => 4,
'080703' => 4,
'080734' => 2,
'080819' => 2,
'088341' => 2,
'805238' => 20,
'805283' => 4,
'805290' => 2,
'805849' => 2,
'806051' => 2,
'806068' => 2,
);
$test = array(
'080604' => 2,
'080703' => 4,
'080819' => 1,
'088341' => 2,
'805238' => 20,
'805283' => 4,
'805290' => 2,
'805849' => 2,
'806051' => 2,
'806068' => 2,
);
They are quite similar, but can have some various differences, e.g. it's possible that:
- some keys of $reference are not present in $test at all
- some keys of $test are not present in $reference at all
- all keys are present, but the values in $reference and $test are different (sometimes $reference value is bigger than $test and sometimes the value of $test is bigger than $reference)
I need to find out the differences automatically and to output them in a way, that not only the difference in count itself, but also a description is provided, e.g.
$result = [
'080604' => [
'reference' => 4,
'test' => 2
]
];
If some value is in only one of the lists:
$result = [
'1234567890' => [
'reference' => 0,
'test' => 2
]
];
or something like that.
Does someone have an idea, which is the best way to accomplish this in an elegant way? Thank you very much!
Iterate over each and populate the array with values if present:
$combined = [];
foreach ($reference as $key => $val) {
$combined[$key] = [
'test' => 0,
'reference' => $val,
];
}
foreach ($test as $key => $val) {
if (!isset($combined[$key])) {
$combined[$key] = [
'reference' => 0,
'test' => 0,
]
}
$combined[$key]['test'] = $val;
}
$combined will contain both values from both arrays with reference to both the elements from $reference and $test.
try
$result = array_diff($reference, $test);
print_r($result)

Sort an Array alphabeticaly but move those with 0 values to the end

I need to sort my multidimensional array using two rules.
My array looks like this:
$array = [
['label' => 12, 'countValue' => 5],
['label' => 4, 'countValue' => 78],
['label' => 9, 'countValue' => 5],
['label' => 64, 'countValue' => 0],
['label' => 3, 'countValue' => 60],
['label' => 19, 'countValue' => 0],
['label' => 7, 'countValue' => 5],
];
I need rows with ['countValue'] = 0 to be move to the back and
Sort the rows based on their label value in an ascending direction.
Desired result:
$array = [
['label' => 3, 'countValue' => 60],
['label' => 4, 'countValue' => 78],
['label' => 7, 'countValue' => 5],
['label' => 9, 'countValue' => 5],
['label' => 12, 'countValue' => 5],
['label' => 19, 'countValue' => 0],
['label' => 64, 'countValue' => 0],
];
I have the following code:
public function sortOptionsByName($a, $b)
{
$x = trim($a['label']);
$y = trim($b['label']);
if ($x == '') return 1;
if ($y == '') return -1;
if (is_numeric($x) && is_numeric($y)){
if ($x == $y)
return 0;
return ($x > $y ? 1 : -1);
}
else {
return strcasecmp($x, $y);
}
}
public function sortOptionsByCounts($a, $b)
{
if ($a['countValue'] == $b['countValue']) {
return 0;
}
return ($a['countValue'] < $b['countValue'] ? 1 : -1);
}
Something like...
public function sortOptionsByCountsAndByName($a, $b)
{
if ($a['countValue'] == 0 && $b['countValue'] == 0) {
return -2
}
else {
$this->sortOptionsByName($a, $b)
}
}
First compare values with zero. PHP casts boolean to integer, so you can just subtract to get -1, 0 , 1. And then compare another value when thw 1st comparing returns 0
public function sortOptionsByCountsAndByName($a, $b)
{
$res = ($a['countValue'] == 0) - ($b['countValue'] == 0);
return ($res ? $res : $this->sortOptionsByName($a, $b));
}
You need to sort your data twice, once by name and once by count value. I would suggest to use usort() and implement two custom compare methods:
<?php
function cmpName($a, $b) {
return strnatcmp($a['Name'], $b['Name']);
}
function cmpCountValue($a, $b) {
if ($a['CountValue'] == 0)
return 1;
if ($b['CountValue'] == 0)
return -1;
return cmpName($a, $b);
}
$a[0]['Name'] = 'Bob';
$a[0]['CountValue'] = 0;
$a[1]['Name'] = 'Christal';
$a[1]['CountValue'] = 42;
$a[2]['Name'] = 'Alice';
$a[2]['CountValue'] = 23;
$a[3]['Name'] = 'Jack';
$a[3]['CountValue'] = 1;
$a[4]['Name'] = 'Alex';
$a[4]['CountValue'] = 58;
usort($a, "cmpName");
usort($a, "cmpCountValue");
foreach ($a as $item) {
echo $item['Name'] . ": " . $item['CountValue'] . "<br />";
}
?>
The output is:
Alex: 58
Alice: 23
Christal: 42
Jack: 1
Bob: 0
Online demo: See here
Just bake your sorting rules into two arrays with symmetric elements and separate them with a spaceship operator.
When sorting ASC, false comes before true, so by checking if the countValue is "falsey", true evaluations will be pushed to the back of the pack.
The order of the elements in the sorting callback says:
Prioritize non-zero countValue rows.
When there is a tie on the first rule, break the tie by sorting on label ASC.
Code: (Demo)
usort(
$array,
fn($a, $b) =>
[!$a['countValue'], $a['label']]
<=>
[!$b['countValue'], $b['label']]
);
var_export($array);
Output:
array (
0 =>
array (
'label' => 3,
'countValue' => 60,
),
1 =>
array (
'label' => 4,
'countValue' => 78,
),
2 =>
array (
'label' => 7,
'countValue' => 5,
),
3 =>
array (
'label' => 9,
'countValue' => 5,
),
4 =>
array (
'label' => 12,
'countValue' => 5,
),
5 =>
array (
'label' => 19,
'countValue' => 0,
),
6 =>
array (
'label' => 64,
'countValue' => 0,
),
)

Every possible combos for an island in Hashi game

Here's the game : Hashiwokakero.
To solve it, I need to find all possible combinations of links + islands around every island.
Constraints are, 1 or 2 links, and one combination is enough links created for the targeted island.
For example, I have a island ['A' => 3], which means it needs 3 links to be solved, and it has 3 neighbors ['B', 'C', 'D'].
I'd like to find an algorithm which would produce such an array :
[
['B' => 1, 'C' => 1, 'D' => 1],
['B' => 1, 'C' => 2],
['B' => 1, 'D' => 2],
['B' => 2, 'C' => 1],
['B' => 2, 'D' => 1],
['C' => 1, 'D' => 2],
['C' => 2, 'D' => 1]
];
Thanks.
If you want to find all combinations of links (0, 1, or 2) per neighbor, with a fixed total number of links, then you could use the following recursive function:
function getPossibleLinks($value, $neighbors) {
if ($value == 0) return [[]];
$max = min(2, $value);
$min = 2 - min(count($neighbors) * 2 - $value, 2);
if ($min > 2) {
throw new Exception('Not possible to assign that many links');
}
$results = [];
for ($count = $min; $count <= $max; $count++) {
$nextResults = getPossibleLinks($value - $count, array_slice($neighbors, 0, -1));
foreach($nextResults as $result) {
if ($count) $result[end($neighbors)] = $count;
$results[] = $result;
}
}
return $results;
}
You would need to pass it the number of links as first argument ($value), and the array of neighbors as an array of strings.
Here is an example call:
$results = getPossibleLinks(3, ["B", "C", "D"]);
After this call, $results will have this content:
[
['B' => 2, 'C' => 1],
['B' => 1, 'C' => 2],
['B' => 2, 'D' => 1],
['B' => 1, 'C' => 1, 'D' => 1],
['C' => 2, 'D' => 1],
['B' => 1, 'D' => 2],
['C' => 1, 'D' => 2]
]
See it run on eval.in.

PHP - sort array by month and year

This is my array:
$ar = array(
'Jan-2016' => 1,
'Feb-2016' => 2,
'Mar-2016' => 3,
'Apr-2016' => 4,
'May-2016' => 4,
'Jun-2016' => 4,
'Jul-2016' => 4,
'Aug-2016' => 4,
'Sep-2016' => 4,
'Oct-2016' => 4,
'Nov-2016' => 4,
'Dec-2016' => 4,
'Jan-2015' => 1,
'Mar-2015' => 1);
I want to sort this array by month & year. I've tried below code:
ksort($ar);
But i can't get result as i expected.
I need result as below array:
$ar = array(
'Jan-2015' => 1,
'Mar-2015' => 1,
'Jan-2016' => 1,
'Feb-2016' => 2,
'Mar-2016' => 3,
'Apr-2016' => 4,
'May-2016' => 4,
'Jun-2016' => 4,
'Jul-2016' => 4,
'Aug-2016' => 4,
'Sep-2016' => 4,
'Oct-2016' => 4,
'Nov-2016' => 4,
'Dec-2016' => 4);
uksort is the right function to use :
uksort($ar, function($a1, $a2) {
$time1 = strtotime($a1);
$time2 = strtotime($a2);
return $time1 - $time2;
});
print_r($ar);
Supplement: Sort Order
$sortOrder = true; // ASC
$sortOrder = false; // DESC
uksort($array, function($a, $b) use($sortOrder) {
$timeA = strtotime($a);
$timeB = strtotime($b);
return $sortOrder ? ($timeA - $timeB) : ($timeB - $timeA);
});

PHP - More efficient method for sorting alphanumeric keys

NOTE: I edited the example and replaced all values with 1. The values do not matter, only the keys do. The previous values I had written led to a misunderstanding. Sorry.
NOTE: a/b blocks are always continuous. I'm adding this because it wasn't clear. If there is 3a/3b and 5a/5b there will always be 4a/4b and not only 4.
I have arrays that contain numbered keys with leading zeros. Sometimes, these numbered keys have two variations that I distinguish by using the suffixes a and b. The number of keys with or without variations is unknown, however there is never more than 2 digits; i.e. the highest numerical key is '09'.
The problem is that these array keys need to be sorted numerically, but when suffixes are present, those should take precedence. Using ksort() alone does not achieve this.
For example, ksort() gives me this:
$arr = array(
'01' => 1,
'02' => 1,
'03a' => 1,
'03b' => 1,
'04a' => 1,
'04b' => 1,
'05a' => 1,
'05b' => 1,
'06' => 1,
);
But, I need this:
$arr = array(
'01' => 1,
'02' => 1,
'03a' => 1,
'04a' => 1,
'05a' => 1,
'03b' => 1,
'04b' => 1,
'05b' => 1,
'06' => 1,
);
I use some fancy coding gymnastics to get what I want, but it isn't pretty. I'm wondering if there's a better, cleaner way?
Here is what I do.
1) I use ksort() which gives me the first of the two arrays above. (The one which isn't yet what I want.)
2) I create two arrays. One for the 'a' suffixes, the other for the 'b' suffixes.
$arr_a = array();
$arr_b = array();
foreach ($arr as $k => $v) {
if (substr($k, 2) == 'a') {
$arr_a[$k] = $v;
} else if (substr($k, 2) == 'b') {
$arr_b[$k] = $v;
}
}
3) I merge the two suffix arrays.
$arr_suffixes = array_merge($arr_a, $arr_b);
4) I slice up my original array so that I get the part before the suffixes, and the part after the suffixes.
$i = array_search(key($arr_suffixes), array_keys($arr));
$length = count($arr_suffixes);
$arr_before_suffixes = array_slice($arr, 0, $i);
$arr_after_suffixes = array_slice($arr, $i + $length);
5) Using array_merge, I recombine the sliced arrays to create the array I need.
$arr = array_merge($arr_before_suffixes, $arr_suffixes);
$arr = array_merge($arr, $arr_after_suffixes);
Finally, we have the correct $arr. Isn't there a better way to do this? It feels really ugly.
You have no formalized rule. I'll try to guess.
$arr = array(
'01' => 1,
'02' => 1,
'03a' => 1,
'03b' => 1,
'04a' => 1,
'04b' => 1,
'05a' => 1,
'05b' => 1,
'06' => 1,
);
uksort($arr, function($item1, $item2)
{
$last1 = substr($item1, -1);
$last2 = substr($item2, -1);
// one of the items is a number or last letters matches
if (is_numeric($last1) || is_numeric($last2) || $last1 == $last2)
// simple number comparison
return $item1 - $item2;
else
// natural order comparison
return $last1 > $last2 ? 1 : -1;
});
var_dump($arr);
natsort() function will helps you:
$arr = array(
'01' => 1,
'02' => 1,
'03a' => 1,
'03b' => 1,
'04a' => 1,
'04b' => 1,
'05a' => 1,
'05b' => 1,
'06' => 1,
);
$keys = array_keys($arr);
natsort($keys);
$result = array();
foreach ($keys as $key) {
$result[$key] = $arr[$key];
}
print_r($result); // Your expected result
$arr = array(
'01' => 1,
'02' => 2,
'03a' => 3,
'03b' => 6,
'04a' => 4,
'04b' => 7,
'05a' => 5,
'05b' => 8,
'06' => 9,
);
uksort(
$arr,
function($a, $b) {
sscanf($a, '%d%s', $an, $as);
sscanf($b, '%d%s', $bn, $bs);
if ($as === null || $bs === null || $as === $bs) {
return $an - $bn;
}
return strcmp($as, $bs);
}
);
var_dump($arr);

Categories