'Filter array by skipping every nth element from the end

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);


Solution 1:[1]

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;
}

Solution 2:[2]

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.

Solution 3:[3]

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

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3