How to push with a regularity elements in an array php - php

I have an array of posts ids and an array of videos ids. I need to mix both to have something like "First is a video, then 3 normal posts then another video then 5 normal posts.. repeat... until the end. (ie : Video,normal,normal,normal,Video,normal*5 and repeat..)
My code works but how can i make it more clean and concise ?
$a1 = $video_posts_arr;
$a2 = $standard_posts_arr;
$res = [];
$tc1 = count($a1);
$tc2 = count($a2);
$tc = $tc1 + $tc2;
$i1 = 0;
$i2 = 0;
for ($i = 0; $i < $tc; $i++) {
// first video
if (isset( $a1[$i1] )) {
array_push($res,$a1[$i1]);
$i1++;
}
// next 3 normals
if (isset( $a2[$i2]) ) {
array_push($res,$a2[$i2]);
$i2++;
array_push($res,$a2[$i2]);
$i2++;
array_push($res,$a2[$i2]);
$i2++;
}
// next video
if (isset( $a1[$i1] )) {
array_push($res,$a1[$i1]);
$i1++;
}
// next 5 normals
if (isset( $a2[$i2]) ) {
array_push($res,$a2[$i2]);
$i2++;
array_push($res,$a2[$i2]);
$i2++;
array_push($res,$a2[$i2]);
$i2++;
array_push($res,$a2[$i2]);
$i2++;
array_push($res,$a2[$i2]);
$i2++;
}
}```

It's kind of hard to tell from your wording, but I'm assuming you're wanting to go back and forth between 3 "regular" posts, then 5, then 3, etc, between each "video" post. So as we loop through the video posts, we have to toggle back and forth between those options, and then use that plus a marker/counter to know where to insert each video post in the list of regular posts.
$a1 = array("v1", "v2", "v3", "v4", "v5");
$a2 = array("p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", "p14", "p15", "p16");
$skipNum = 3;
$currentPos = 1;
array_unshift($a2, array_shift($a1));
foreach($a1 as $v)
{
$currentPos+= $skipNum;
array_splice($a2, $currentPos, 0, $v);
$currentPos++;
if($skipNum == 3)
{
$skipNum = 5;
}
else
{
$skipNum = 3;
}
}
var_dump($a2);
DEMO

how can i make it more clean and concise ?
Here's what I think you should do...
Iterate on the video array and inject each video element into the standard post array at the variable positions without making any replacements.
Code: (Demo)
$video_posts_arr = range('a','e');
$standard_posts_arr = range(1, 16);
$pos = 0;
foreach ($video_posts_arr as $index => $video) {
array_splice($standard_posts_arr, $index + $pos, 0, $video);
$pos += $index & 1 ? 5 : 3;
}
var_export($standard_posts_arr);
Using the $index of the videos array spares having to use incrementation while iterating.
I am using a ternary expression with a bitwise condition to increase the $pos based on whether $index is odd. I should express these variables to better explain what is happening. As shown in this Demo, these are the values generated as $index increases:
$index = 0, $pos = 0; splicePoint = 0, isOdd = false
$index = 1, $pos = 3; splicePoint = 4, isOdd = true
$index = 2, $pos = 8; splicePoint = 10, isOdd = false
$index = 3, $pos = 11; splicePoint = 14, isOdd = true
$index = 4, $pos = 16; splicePoint = 20, isOdd = false
Output:
array (
0 => 'a',
1 => 1,
2 => 2,
3 => 3,
4 => 'b',
5 => 4,
6 => 5,
7 => 6,
8 => 7,
9 => 8,
10 => 'c',
11 => 9,
12 => 10,
13 => 11,
14 => 'd',
15 => 12,
16 => 13,
17 => 14,
18 => 15,
19 => 16,
20 => 'e',
)
The above script is demonstrated using a ratio of videos to standard posts which results in no consecutive videos.
If your video to standard post ratio is too great, then the above script will simply append all remaining/extra videos to the end of the standard posts array.
If you would like to ensure that the final element in your standard posts array is not a video, then you can add a conditional break like this:
$pos = 0;
foreach ($video_posts_arr as $index => $video) {
$injection_pos = $index + $pos;
if (!isset($standard_posts_arr[$injection_pos])) {
break;
}
array_splice($standard_posts_arr, $injection_pos, 0, $video);
$pos += $index & 1 ? 5 : 3;
}
Or to allow a maximum of one video after the last standard post, move the break after the array_splice() call and account for the injected video element like this:
$pos = 0;
foreach ($video_posts_arr as $index => $video) {
$injection_pos = $index + $pos;
array_splice($standard_posts_arr, $injection_pos, 0, $video);
if (!isset($standard_posts_arr[$injection_pos + 1])) {
break;
}
$pos += $index & 1 ? 5 : 3;
}

Maybe something like that?
This solution uses array_merge and array_slice to create result. This methods does not edit original arrays, just creates new one based on arguments.
$a1 = array("v1", "v2", "v3");
$a2 = array("a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10");
$output = array_merge(
array_slice($a1, 0, 1), //take one arg from a1. Not smartest to be honest but works. Maybe someone will give better idea here.
array_slice($a2, 0, 3), //take first 3 elements from a2
array_slice($a1, 1, 1),
array_slice($a2, 3, 5),
array_slice($a2, 300, 500), //it is save :)
);
print_r($output);
# [0] => v1
# [1] => a1
# [2] => a2
# [3] => a3
# [4] => v2
# [5] => a4
# [6] => a5
# [7] => a6
# [8] => a7
# [9] => a8
# [10] => a9
# [11] => a10
array_slice that takes only one argument array_slice($a1, 0, 1) looks bit cryptic but at least it is save to use.
This'll create new array instead of editing existing one. It comes with some additional overhead (You probably should not worry about it).
http://sandbox.onlinephpfunctions.com/code/9b23381b31ab7ed48f1941cbca981071eaf250af

Related

PHP - get array value if next element is not series

Here is my array
$array = array( 0 => 10, 1 => 9, 2 => 8, 3 => 6, 4=> 4 );
I want to get array value 6. because, 7 is missing before this value/series is break.
Please help me how can I do it easy & fast method.
The sequence could be calculated by subtracting the first value from the second value. Then you could for example use a for loop to loop through the values of the array and check if there is also a next value available by checking if there is an index + 1.
Then if that is the case you can subtract the current value in the loop from the next value and check if that result equals the step size.
If that is not the case, the next value of the iteration is the value that breaks the sequence and you can break out of the loop.
$array = [10,9,8,6,4];
if (count($array) > 2) {
$step = $array[0] - $array[1];
for ($i = 0; $i < count($array); $i++) {
if (isset($array[$i + 1]) && $array[$i] - $array[$i + 1] !== $step) {
$wrongValue = $array[$i + 1];
echo sprintf(" The step count is %d, but after %d comes %d which breaks the sequence.",
$step, $array[$i], $wrongValue
);
break;
}
}
}
Demo
<?php
$sequence =
[
0 => 10,
1 => 9,
3 => 8,
4 => 6,
5 => 4
];
$last = null;
foreach($sequence as $k => $v)
{
if(!is_null($last) && $last - $v > 1)
break;
$last = $v;
}
var_dump($k, $v);
Output:
int(4)
int(6)
Loop through it, save the previous values and compare with current one:
<?php
function findMissing($array) {
$missing = [];
foreach($array as $key => $val) {
if(isset($previousValue) && $previousValue-1!=$val) {
echo "not in series: ".($previousValue-1) .", returning ".$val."<br>\n";
$missing[] = $val;
}
$previousValue=$val;
}
return $missing;
}
// USAGE:
$array = array( 0 => 10, 1 => 9, 2 => 8, 3 => 6, 4=> 4 );
findMissing($array);
// not in series: 7, returning 6
// not in series: 5, returning 4
$array2 = array( 10, 9, 8, 6, 5 );
$missingValues = findMissing($array2);
// not in series: 7, returning 6
var_dump($missingValues);
// array(1) { [0]=> int(6) }
I'm not sure I understand what you want to achieve, but let's start it here.
UPDATED:
for($i = 0; $i < count($array); ++$i) {
if($i > 0) {
if($array[$i] != ($array[$i-1]-1)) {
echo($array[$i]);
break;
}
}
}
Output:
6

Is there an elegant way to count values of an array over a given interval/step to generate histogram data?

I want to get generate histogram data for the following array. array_count_values() would be great to use, except it just counts values for an exact value match. How can I elegantly do the same, but bundle values by a given step or interval?
$dataArray = array(385,515,975,1136,2394,2436,4051,4399,4484,4768,4768,4849,4856,4954,5020,5020,5020,5020,5020,5020,5020,5020,5020,5052,5163,5200,5271,5421,5421,5442,5746,5765,5903,5992,5992,6046,6122,6205,6208,6239,6310,6360,6416,6512,6536,6543,6581,6609,6696,6699,6752,6796,6806,6855,6859,6886,6906,6911,6923,6953,7016,7072,7086,7089,7110,7232,7278,7293,7304,7309,7348,7367,7378,7380,7419,7453,7454,7492,7506,7549,7563,7721,7723,7731,7745,7750,7751,7783,7791,7813,7813,7814,7818,7833,7863,7875,7886,7887,7902,7907,7935,7942,7942,7948,7973,7995,8002,8013,8013,8015,8024,8025,8030,8038,8041,8050,8056,8060,8064,8071,8081,8082,8085,8093,8124,8139,8142,8167,8179,8204,8214,8223,8225,8247,8248,8253,8258,8264,8265,8265,8269,8277,8278,8289,8300,8312,8314,8323,8328,8334,8363,8369,8390,8397,8399,8399,8401,8436,8442,8456,8457,8471,8474,8483,8503,8511,8516,8533,8560,8571,8575,8583,8592,8593,8626,8635,8635,8644,8659,8685,8695,8695,8702,8714,8715,8717,8729,8732,8740,8743,8750,8756,8772,8772,8778,8797,8828,8840,8840,8843,8856,8865,8874,8876,8878,8885,8887,8893,8896,8905,8910,8955,8970,8971,8991,8995,9014,9016,9042,9043,9063,9069,9104,9106,9107,9116,9131,9157,9227,9359,9471);
// if array_count_values accepted a step value I could do this:
print_r(array_count_values($dataArray,1000));
// expected result:
// array(1000 => 3, 2000 => 1, ... 10000 => 15);
// ^0-1000 ^[385,515,975]
What do you recommend?
In the event I have to loop through all values manually, is there an elegant way to round all values to a given interval?
$step = 1000;
$result = array_count_values(
array_map(
function ($value) use ($step) {
return (int) ceil($value / $step) * $step;
},
$dataArray
)
);
var_dump($result);
The rounding solution seems pretty straight forward:
$step_size = 10;
$data = array(10, 20, 24, 30, 35, 50);
foreach ($data as $index => $value) {
$data[$index] = round($value / $step_size) * $step_size;
}
// array(10, 20, 20, 30, 40, 50);
You can build the output directly to avoid mapping the entire data array just to make use of array_count_values(); below is a more generic implementation that allows the mapping to be done outside of the function itself:
function array_count_values_callback(array $data, callable $fn)
{
$freq = [];
foreach ($data as $item) {
$key = $fn($item);
$freq[$key] = isset($freq[$key]) ? $freq[$key] + 1 : 1;
}
return $freq;
}
print_r(array_count_values_callback($dataArray, function($item) {
return ceil($item / 1000) * 1000;
}));
Here is a simple solution where you loop through your $dataArray,
$step_size = 1000;
$histogramArray = array();
foreach ($dataArray as $v) {
$k = (int)ceil($v / $step_size) * $step_size;
if (!array_key_exists($k, $histogramArray)) $histogramArray[$k] = 0;
$histogramArray[$k]++;
}
And the output would be,
Array
(
[1000] => 3
[2000] => 1
[3000] => 2
[5000] => 8
[6000] => 21
[7000] => 25
[8000] => 46
[9000] => 110
[10000] => 15
)

how to change the array key to start from 1 instead of 0

I have values in some array I want to re index the whole array such that the the first value key should be 1 instead of zero i.e.
By default in PHP the array key starts from 0. i.e. 0 => a, 1=> b, I want to reindex the whole array to start from key = 1 i.e 1=> a, 2=> b, ....
$alphabet = array("a", "b", "c");
array_unshift($alphabet, "phoney");
unset($alphabet[0]);
Edit: I decided to benchmark this solution vs. others posed in this topic. Here's the very simple code I used:
$start = microtime(1);
for ($a = 0; $a < 1000; ++$a) {
$alphabet = array("a", "b", "c");
array_unshift($alphabet, "phoney");
unset($alphabet[0]);
}
echo (microtime(1) - $start) . "\n";
$start = microtime(1);
for ($a = 0; $a < 1000; ++$a) {
$stack = array('a', 'b', 'c');
$i= 1;
$stack2 = array();
foreach($stack as $value){
$stack2[$i] = $value;
$i++;
}
$stack = $stack2;
}
echo (microtime(1) - $start) . "\n";
$start = microtime(1);
for ($a = 0; $a < 1000; ++$a) {
$array = array('a','b','c');
$array = array_combine(
array_map(function($a){
return $a + 1;
}, array_keys($array)),
array_values($array)
);
}
echo (microtime(1) - $start) . "\n";
And the output:
0.0018711090087891
0.0021598339080811
0.0075368881225586
Here is my suggestion:
<?php
$alphabet = array(1 => 'a', 'b', 'c', 'd');
echo '<pre>';
print_r($alphabet);
echo '</pre>';
?>
The above example will output:
Array
(
[1] => a
[2] => b
[3] => c
[4] => d
)
Simply try this
$array = array("a","b","c");
array_unshift($array,"");
unset($array[0]);
Ricardo Miguel's solution works best when you're defining your array and want the first key to be 1. But if your array is already defined or gets put together elsewhere (different function or a loop) you can alter it like this:
$array = array('a', 'b', 'c'); // defined elsewhere
$array = array_filter(array_merge(array(0), $array));
array_merge will put an array containing 1 empty element and the other array together, re-indexes it, array_filter will then remove the empty array elements ($array[0]), making it start at 1.
$array = array('a', 'b', 'c', 'd');
$array = array_combine(range(1, count($array)), array_values($array));
print_r($array);
the result:
Array
(
[1] => a
[2] => b
[3] => c
[4] => d
)
If you are using a range, try this code:
$data = array_slice(range(0,12), 1, null, true);
// Array ( [1] => 1 [2] => 2 [3] => 3 [4] => 4 [5] => 5 [6] => 6 [7] => 7 [8] => 8 [9] => 9 [10] => 10 [11] => 11 [12] => 12 )
I see this answer is very old, but my solution for this is by adding a +1 to your index. I'll do that because I think this is much faster and easy to read. When you print this it will start from 1 because 0+1 =1, then 2 etc.
foreach($items as $index => $item){
echo $index+1 . $item
}
I think it is simple as that:
$x = array("a","b","c");
$y =array_combine(range(1, count($x)), $x);
print_r($y);
$data = ['a', 'b', 'c', 'd'];
$data = array_merge([''], $data);
unset($data[0]);
I have found that this will perform slightly better, than the accepted solution, on versions of PHP 7.1+.
Benchmark code:
$start = microtime(1);
for ($a = 0; $a < 10000; ++$a) {
$data = ['a', 'b', 'c', 'd'];
$data = array_merge([''], $data);
unset($data[0]);
}
echo (microtime(1) - $start) . "\n";
$start = microtime(1);
for ($a = 0; $a < 10000; ++$a) {
$data = ['a', 'b', 'c', 'd'];
array_unshift($data, '');
unset($data[0]);
}
echo (microtime(1) - $start) . "\n";
Scripts execution time (PHP 7.4):
0.0011248588562012
0.0017051696777344
And the difference of these benchmarks will increase as the number of array values increases.
If you already have an array and want to reindex it
to start from index X instead of 0, 1, 3...N then:
// Check if an array is filled by doing this check.
if (count($your_array) > 0) {
// Let's say we want to start from index - 5.
$your_array = [5 => $your_array[0], ...array_slice($your_array, 1)];
}
About spread operator "..."
https://www.php.net/manual/en/migration56.new-features.php#migration56.new-features.splat
P.S.
Real-world scenario/use-case, what I've met in work doing a task for a client:
I had one <div> that contains two <tables>. Each <table> contains markup for the days of the week. The first one has days from Monday to Thursday. The second one has days from Friday to Sunday. So, in my task, I had the variable represent a week in which each day had hours of open and close. I needed appropriately divide that week's variable into two.
<table>
<?php for ($dayIndex = 0; $dayIndex < 4; $dayIndex++): ?>
<?php
$_timetable = array_slice($timetable, 0, 4);
// $renderTimetableRow is an anonymous function
// that contains a markup to be rendered, like
// a html-component.
$renderTimetableRow($_timetable, $dayIndex);
?>
<?php endfor; ?>
</table>
<table>
<?php for($dayIndex = 4; $dayIndex < 7; $dayIndex++): ?>
<?php
if (count($_timetable = array_slice($timetable, 4, 7)) > 0) {
$_timetable = [4 => $_timetable[0], ...array_slice($_timetable, 1)];
}
// $renderTimetableRow is an anonymous function
// that contains a markup to be rendered, like
// a html-component.
$renderTimetableRow($_timetable, $dayIndex);
?>
<?php endfor; ?>
</table>
try this
<?php
$stack = array('a', 'b', 'c', 'd');
$i= 1;
foreach($stack as $value){
$stack2[$i] = $value;
$i++;
}
$stack = stack2;
?>

Help with refactoring PHP code

I had some troubles implementing Lawler's algorithm but thanks to SO and a bounty of 200 reputation I finally managed to write a working implementation:
Lawler's Algorithm Implementation Assistance
I feel like I'm using too many variables and loops there though so I am trying to refactor the code. It should be simpler and shorter yet remain readable.
Does it make sense to make a class for this? Any advice or even help with refactoring this piece of code is welcomed:
<?php
/*
* #name Lawler's algorithm PHP implementation
* #desc This algorithm calculates an optimal schedule of jobs to be
* processed on a single machine (in reversed order) while taking
* into consideration any precedence constraints.
* #author Richard Knop
*
*/
$jobs = array(1 => array('processingTime' => 2,
'dueDate' => 3),
2 => array('processingTime' => 3,
'dueDate' => 15),
3 => array('processingTime' => 4,
'dueDate' => 9),
4 => array('processingTime' => 3,
'dueDate' => 16),
5 => array('processingTime' => 5,
'dueDate' => 12),
6 => array('processingTime' => 7,
'dueDate' => 20),
7 => array('processingTime' => 5,
'dueDate' => 27),
8 => array('processingTime' => 6,
'dueDate' => 40),
9 => array('processingTime' => 3,
'dueDate' => 10));
// precedence constrainst, i.e job 2 must be completed before job 5 etc
$successors = array(2=>5,
7=>9);
$n = count($jobs);
$optimalSchedule = array();
for ($i = $n; $i >= 1; $i--) {
// jobs not required to precede any other job
$arr = array();
foreach ($jobs as $k => $v) {
if (false === array_key_exists($k, $successors)) {
$arr[] = $k;
}
}
// calculate total processing time
$totalProcessingTime = 0;
foreach ($jobs as $k => $v) {
if (true === array_key_exists($k, $arr)) {
$totalProcessingTime += $v['processingTime'];
}
}
// find the job that will go to the end of the optimal schedule array
$min = null;
$x = 0;
$lastKey = null;
foreach($arr as $k) {
$x = $totalProcessingTime - $jobs[$k]['dueDate'];
if (null === $min || $x < $min) {
$min = $x;
$lastKey = $k;
}
}
// add the job to the optimal schedule array
$optimalSchedule[$lastKey] = $jobs[$lastKey];
// remove job from the jobs array
unset($jobs[$lastKey]);
// remove precedence constraint from the successors array if needed
if (true === in_array($lastKey, $successors)) {
foreach ($successors as $k => $v) {
if ($lastKey === $v) {
unset($successors[$k]);
}
}
}
}
// reverse the optimal schedule array and preserve keys
$optimalSchedule = array_reverse($optimalSchedule, true);
// add tardiness to the array
$i = 0;
foreach ($optimalSchedule as $k => $v) {
$optimalSchedule[$k]['tardiness'] = 0;
$j = 0;
foreach ($optimalSchedule as $k2 => $v2) {
if ($j <= $i) {
$optimalSchedule[$k]['tardiness'] += $v2['processingTime'];
}
$j++;
}
$i++;
}
echo '<pre>';
print_r($optimalSchedule);
echo '</pre>';
I would make it a class. I find it easier to refactor an algorithm when all necessary variables are encapsulated as class members, rather than remembering what values I have to pass in and out every time I extract a method.
You should set your inputs to the algorithm in the constructor and then have a generic execute method. This would allow you to conform to both the command and strategy patterns more easily.
Make all your loop and conditional bodies into individual protected functions. With appropriate naming, that will increase the readability immensely, and make it much easier to alter the algorithm through inheritance.

Process every two elements in a flat array

I have an array something like this..
[0] => english
[1] => 85
[2] => mathematics
[3] => 75
[4] => science
[5] => 71
[6] => social
[7] => 92
I want all values with even indexes to go as keys and all values odd indexes to go values. Like this
[english] => 85,
[mathematics] => 75
[science] => 71
[social] => 92
Is there any good function to achieve this ? or can you help me with the code ?
A simple loop will do this:
$input = array(
'english',
85,
'mathematics',
75,
'science',
71,
'social',
92,
);
$output = array();
for ($i=0; $i<count($input); $i+=2) {
$output[$input[$i]] = $input[$i+1];
}
print_r($output);
Note: the above code assumes there are an even number of elements.
Something like this:
<?php
$arr[0] = 'english';
$arr[1] = 85;
$arr[2] = 'mathematics';
$arr[3] = 75;
$arr[4] = 'science';
$arr[5] = 71;
$arr[6] = 'social';
$arr[7] = 92;
for($i=0;$i<count($arr);$i++)
{
if($i & 1)
$odd[] = $arr[$i];
else
$even[] = $arr[$i];
}
$result = array_combine($even,$odd);
var_dump($result);
?>
Output:
array(4) {
["english"]=>
int(85)
["mathematics"]=>
int(75)
["science"]=>
int(71)
["social"]=>
int(92)
}
Solution using array_chunk function
$arr = array('english', 85,
'mathematics', 75,
'science', 71,
'social', 92
);
$result = array();
$chunks = array_chunk($arr, 2);
foreach ($chunks as $value) {
$result[$value[0]] = $value[1];
}
In functional style just for the kick (not considering performance):
$odd = function($value) {
return($value & 1);
};
$even = function($value) {
return(!($value & 1));
};
$oddArr = array_filter($arr, $odd));
$evenArr = array_filter($arr, $even));
$ans = array_combine($oddArr,$evenArr);

Categories