Is there an efficient way to get an array by skipping every n elements starting from the end (so that the last element is always in the result)?
Basically, I have a large array of 300k elements that I want to turn to 100k or 150k.
Sample input:
$array = array(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
Test: skip every other element
$x = 1;
Expected output:
$new_array = array(1,3,5,7,9,11,13,15);
Test: skip every second element
$x = 2;
Expected output:
$new_array = array(0,3,6,9,12,15);
Test: skip every third element
$x = 3;
Expected output:
$new_array = array(3,7,11,15);
Use a for() loop with a decrementing counter to allow you to push qualifying elements into the result array starting from the end of the array. To put the result array in the original order, just call array_reverse() after the loop.
Code: (Demo)
function skipFromBack(array $array, int $skip): array {
$result = [];
for ($index = array_key_last($array); $index > -1; $index -= 1 + $skip) {
$result[] = $array[$index];
}
return array_reverse($result);
}
Alternatively, you can pre-calculate the starting index, use an incrementing counter, and avoid the extra array_reverse() call. (Demo)
function skipFromBack(array $array, int $skip): array {
$increment = $skip + 1;
$count = count($array);
$start = ($count - 1) % $increment;
$result = [];
for ($i = $start; $i < $count; $i += $increment) {
$result[] = $array[$i];
}
return $result;
}
function skip_x_elements($array, $x)
{
$newArray = [];
$skipCount = 0;
foreach ($array as $value) {
if ($skipCount === $x) {
$newArray[] = $value;
$skipCount = 0;
} else {
$skipCount++;
}
}
return $newArray;
}
This should do what you want.
Improving upon #Dieter_Reinert answer, so you can also retain the keys inside said array, here is a slightly more flexible version that better fits the original question:
function skipX($array, $x, $grab = false){
$x = (!$grab) ? $x: $x - 1;
if($x <= 0) return $array;
$count = (count($array) % $x == 0) ? 0:$x;
$temparr = [];
foreach($array as $key => $value){
if($count === $x){
$temparr[$key] = $value;
$count = 0;
}else $count++;
}
return $temparr;
}
Example:
$array = range(0, 15);
foreach ([0, 1, 2, 3] as $skip){
print_r(skipX($array, $skip));
echo "\n---\n";
}
The correct output based on the original question:
Array
(
[0] => 0
[1] => 1
[2] => 2
[3] => 3
[4] => 4
[5] => 5
[6] => 6
[7] => 7
[8] => 8
[9] => 9
[10] => 10
[11] => 11
[12] => 12
[13] => 13
[14] => 14
[15] => 15
)
---
Array
(
[1] => 1
[3] => 3
[5] => 5
[7] => 7
[9] => 9
[11] => 11
[13] => 13
[15] => 15
)
---
Array
(
[2] => 2
[5] => 5
[8] => 8
[11] => 11
[14] => 14
)
---
Array
(
[0] => 0
[4] => 4
[8] => 8
[12] => 12
)
---
Online PHP Sandbox Demo: https://onlinephp.io/c/7db96
I have an Array Array
( [0] => ["1","2","7","8","9"] )
and i want to like that
Array ( [0] => 1 [1] => 2 [2] => 7 [3] => 8 [4] => 9 )
i try foreach loop
foreach($no_a as $key=>$val){
foreach($val as $k=>$v){
$newp_arr[] $v ;
}
}
$result = $no_a[0];
$new_result = array();
for ($i=0; $i < 21 ; $i++) {
# code...
$num = $result[$i];
if(is_numeric($num))
{
$new_result[] = $result[$i];
}
}
print_r($new_result);
I have two arrays, Array 1 being a donor array which has a series of values (not necessarily equal values as in my example). Array 2 is the desired result and would store a series of sub arrays with values from Array 1 where each sub array's total sum would not exceed 25. If it does, the excess would get pushed to the next index in Array 2 where the rules would also apply.
Donor array (Array 1):
$a1=array(10,10,10,10,10,10,10,10,10,10);
Desired output (Array 2):
Array
(
[0] => 10,10,5
[1] => 5,10,10
[2] => 10,10,5
[3] => 5,10,10
)
Here the code I tried but it gets an error:
Notice: Undefined offset: 10...etc.
$a1=array(10,10,10,10,10,10,10,10,10,10);
$a2=array();
$count=count($a1);
for($i=0;$i<$count;$i++){
$a2count=array_sum($a2);
if($a2count>25){
$i=$i+1;
$a2[$i]=$a1[$i];
}
else{
$a2[$i]=$a1[$i];
}
}
print_r($a2);
I don't know what logic to implement and get result I'm looking for.
Mayhaps something like this will work for you. I will notate so it's not just a copy-and-paste answer. Maybe someone will get some insight on it to improve it in the future:
function slitArray($a1,$num = 25)
{
# Used to store the difference when the value exceeds the max
$store = 0;
# Storage container that will be built using sums/diffs
$new = array();
# Loop through the main array
foreach($a1 as $value) {
# If the last key/value pair in our return array is an array
if(is_array(end($new)))
# Add up the values in that array
$sum = array_sum(current($new));
else
# If not array, no values have been stored yet
$sum = 0;
# This just gets the last key
$count = (count($new)-1);
# Assign last key
$i = ($count <= 0)? 0 : $count;
# If the sum of the current storage array plus the value
# of the current array is greater than our max value
if(($sum + $value) > $num) {
# Take max and remove the current total of storage array
$use = ($num-$sum);
# Take what's left and remove it from the current value
$store = ($value-$use);
# If the current stored value (the value we want to push to
# the next storage k/v pair) is more than the max allowed
if($store > $num) {
# Takes a value, checks if it's greater than max,
# and if it is, breaks the value up by max as a divider
$divide = function($store,$num)
{
if($store > $num) {
$count = ceil($store/$num);
for($i=0; $i<$count; $i++) {
$new[] = ($store > $num)? $num : $store;
$store -= $num;
}
return $new;
}
else
return array($store);
};
# This should either be an array with 1 or more values
$forward = $divide($store,$num);
# Do a look forward and add this excess array into our
# current storage array
$a = $i;
foreach($forward as $aVal) {
$new[$a+=1][] = $aVal;
}
}
# If the store value is less than our max value, just add
# it to the next key in this storage array
else {
$new[$i+1][] = $store;
# Reset the storage back to 0, just incase
$store = 0;
}
}
# Set the current "use" value as the current value in our
# from-array. Since it doesn't exceed the max, it just gets
# added to the storage array
else
$use = $value;
# Sometimes the math makes $use 0, keep that out of the
# storage array. The $use value is the current value to add at
# the time of iteration. Previous storage values are added as
# future-keys
if($use > 0)
$new[$i][] = $use;
}
# Return the final assembled array
return $new;
}
# To use, add array into function
$a1 = array(10,10,10,10,10,10,10,10,10,10);
# to split using different max value, just add it to second arg
# example: slitArray($a1,20);
print_r(slitArray($a1));
Gives you:
Array
(
[0] => Array
(
[0] => 10
[1] => 10
[2] => 5
)
[1] => Array
(
[0] => 5
[1] => 10
[2] => 10
)
[2] => Array
(
[0] => 10
[1] => 10
[2] => 5
)
[3] => Array
(
[0] => 5
[1] => 10
[2] => 10
)
)
An array input of:
$a1 = array(23,2,71,23,50,2,3,4,1,2,50,75);
Gives you:
Array
(
[0] => Array
(
[0] => 23
[1] => 2
)
[1] => Array
(
[0] => 25
)
[2] => Array
(
[0] => 25
)
[3] => Array
(
[0] => 21
[1] => 4
)
[4] => Array
(
[0] => 19
[1] => 6
)
[5] => Array
(
[0] => 25
)
[6] => Array
(
[0] => 19
[1] => 2
[2] => 3
[3] => 1
)
[7] => Array
(
[0] => 3
[1] => 1
[2] => 2
[3] => 19
)
[8] => Array
(
[0] => 25
)
[9] => Array
(
[0] => 6
[1] => 19
)
[10] => Array
(
[0] => 25
)
[11] => Array
(
[0] => 25
)
[12] => Array
(
[0] => 6
)
)
Here you go: The logic is not so hard. Hope it helps.
<?php
$a1=array(10,10,10,10,10,10,10,10,10,10);
$a2 = [];
$a3 = [];
$m = 0;
for($i = 0; $i < count($a1); ++$i){
$m += $a1[$i];
if($m > 25){
$n = $m % 25;
if(array_sum($a2) != 25){
$a2[] = $n;
}
$a3[] = implode(',', $a2);
$a2 = [];
$m = $n;
$a2[] = $n;
} else{
$a2[] = $a1[$i];
}
}
$a3[] = implode(',', $a2);
print_r($a3);
?>
The final code for this question
<?php
function slitArray($a1,$num = 25)
{
$store = 0;
$new = array();
foreach($a1 as $value) {
if(is_array(end($new)))
$sum = array_sum(current($new));
else
$sum = 0;
$count = (count($new)-1);
$i = ($count <= 0)? 0 : $count;
if(($sum + $value) > $num) {
$use = ($num-$sum);
$store = ($value-$use);
if($store > $num) {
$divide = function($store,$num)
{
if($store > $num) {
$count = ceil($store/$num);
for($i=0; $i<$count; $i++) {
$new[] = ($store > $num)? $num : $store;
$store -= $num;
}
return $new;
}
else
return array($store);
};
$forward = $divide($store,$num);
$a = $i;
foreach($forward as $aVal) {
$new[$a+=1][] = $aVal;
}
}
else {
$new[$i+1][] = $store;
$store = 0;
}
}
else
$use = $value;
if($use > 0)
$new[$i][] = $use;
}
return $new;
}
$a1 = array(10,20,30,40,50,60);
$arr=slitArray($a1);
print_r($arr);
?>
Let me help you a bit using Pseudocode:
ar1 = {10,10,10,20,40,[0]=>1,[0]=>3,[0]=>4};
ar2 = new array (ar.length) \\ worst case
int c = 0; \\current
foreach (ar1 as $value){
ar2 [c]+=ar1[i];
if (ar2 [c]>25){ c++;}
}
Logic behind the code:
Add the value of ar1[i] to the current ar2 value till it passes your limit (25 in this case). If it exceeds you boundary, than move to the next value in the goal array. The worst case would be, that every value is over 25, so it would be an exact copy of the original array.
Here the php code:
$ar1=array(10,10,10,10,10,10,10,10,10,10);
$ar2 = array(0,0,0,0,0,0,0,0,0,0);
$c = 0;
foreach( $ar1 as $key => $value ){
$ar2[$c]=$value+$ar2[$c];
if ($ar2[$c]>25){$c++;}
}
i have two arrays
$value_array = array('50','40','30','20','10');
$customer = array('300','200','100');
i want to distribute the value array to customers based on the value of customers that is taken as limit.adding values by checking it wont cross the limit that is 300 , 200 and 100.
but customer array not working one direction it should work first forward and then backward like that
i want to produce an array in form of
Array
(
[0] => Array
(
[0] => 50
)
[1] => Array
(
[0] => 40
[1] => 10
)
[2] => Array
(
[0] => 30
[1] => 20
)
)
After completing customer loop first time it should start from last to first. both array count will change , i mean count.
value array should check 50 -> 300 , 40->200, 30->100 then from last ie, 20 ->100, 10->200 etc.
I tried like
$i = 0;
while($i < count($customer)){
foreach($value_array as $k=>$value){
$v = 0;
if($value <= $customer[$i]){
$customer2[$i][] = $value;
unset($value_array[$k]);
$v = 1;
}
if($v ==1){
break;
}
}
//echo $i."<br/>";
if($i == (count($customer)-1) && (!empty($value_array))){
$i = 0;
$customer = array_reverse($customer, true);
}
$i++;
}
echo "<pre>";
print_r($customer2);
$valueArray = array('50','40','30','20','10','0','-11');
$customer = array('300','200','100');
function parse(array $valueArr, array $customerArr)
{
$customerCount = count($customerArr);
$chunkedValueArr = array_chunk($valueArr, $customerCount);
$temp = array_fill(0, $customerCount, array());
$i = 0;
foreach ($chunkedValueArr as $item) {
foreach ($item as $key => $value) {
$temp[$key][] = $value;
}
$temp = rotateArray($temp);
$i++;
}
// if $i is odd
if ($i & 1) {
$temp = rotateArray($temp);
}
return $temp;
}
function rotateArray(array $arr)
{
$rotatedArr = array();
//set the pointer to the last element and add it to the second array
array_push($rotatedArr, end($arr));
//while we have items, get the previous item and add it to the second array
for($i=0; $i<sizeof($arr)-1; $i++){
array_push($rotatedArr, prev($arr));
}
return $rotatedArr;
}
print_r(parse($valueArray, $customer));
returns:
Array
(
[0] => Array
(
[0] => 50
[1] => 0
[2] => -11
)
[1] => Array
(
[0] => 40
[1] => 10
)
[2] => Array
(
[0] => 30
[1] => 20
)
)
I have this array
dev3->content->->mktg->->->pls1->->->pls2->->->config->->splash
I want to convert this string to multidimensional array. like this
Array
(
[0] => dev3
Array (
[0] => ->content
Array (
[0] => ->->mktg
Array(
[0] => ->->->pls1
[1] => ->->->pls2
[2] => ->->->config
)
[1] => ->->splash
)
)
)
Can anyone do this
it does not work if level will be increaed more then +1 on any step
$str = 'dev3->content->->mktg->->->pls1->->->pls2->->->config->->splash';
$in = preg_split('/(?<!>)(?=->)/', $str);
Above we make such array from the input string
Array
(
[0] => dev3
[1] => ->content
[2] => ->->mktg
[3] => ->->->pls1
[4] => ->->->pls2
[5] => ->->->config
[6] => ->->splash
)
continue working
$result = [];
$p = &$result;
$level = 0;
foreach($in as $i) {
// Count next level
$c = substr_count($i, '->');
// if level is not changed
if($c == $level) { $p[] = $i; continue; }
// level increased
if ($c == $level + 1) {
$level++;
$p[] = [$i];
$p = &$p[count($p)-1];
continue;
}
// any level less then achived before
if ($c < $level) {
$p = &$result;
$level = $c;
while($c--)
$p = &$p[count($p)-1];
$p[] = $i;
continue;
}
die("I can't process this input string");
}
print_r($result);
working demo