skip to Main Content

I want to create a callback function to remove from a numeric array the given values.

Given this array: [ 0, 0, 0, 0, 1, 0, 3, 4, 5, 2, 0, ];
I want to create a callable function to be passed to array_filter that will return this array: [1, 0, 3, 4, 5, 2, 0, ];. I have seen in this answer the use of array_filter: Deleting an element from an array in PHP but is not exactly what I need because this option will remove all the 0s from the array and I need to keep the ones that are not leading.

I have created this function that cycles the array and returns the expected array, but I want to use something more efficient:

function removeLeading( int $array_values[], $to_remove = 0 )
{
    $is_leading = true;
    $result = [];
    foreach( $array_values as $value ) {
        if( ( $value == $to_remove ) && ( $is_leading == true ) ) {
            // Do nothing because it is a leading $to_remove
        }
        else {
            $result[] = $value;
            $is_leading = false;
        }
    }
    return $result;
}

2

Answers


  1. array_filter is not the right tool for this job, because it tests every element of the array, and the most efficient algorithm for this task is probably to stop iterating once you hit the first non-zero element.

    Then, rather than copying the remaining elements to the output one by one, you could use array_slice to produce a new array starting at a particular offset.

    $firstElementToKeep = 0;
    foreach ( $input as $value ) {
       // As soon as we find a non-zero element, stop looking
       if ( $value !== 0 ) {
          break;
       }
       // Track the offset we reach
       $firstElementToKeep++;
    }
    $output = array_slice($input, $firstElementToKeep);
    

    With the example input in your question, $firstElementToKeep reaches 4, which is the index of the 1, so we "slice" from there to the end of the array, and get the desired output.

    In most cases, the difference will be completely undetectable, but for a very large array, there might be a noticeable saving from not running as many comparisons, and not having to resize the output array in memory as more elements are added.

    Login or Signup to reply.
  2. Yes, you can do this with array filter. The trick is to accumulate the first values and return false if it is still zero.

    This will work only if you want to left trim a falsy value.

    $filtered = array_filter(
        [0, 0, 0, 0, 1, 0, 3, 4, 5, 2, 0],
        function($value) use (&$sum) { return $sum += $value === 0 ? false : $value; }
    );
    

    gives you

    array (
      4 => 1,
      5 => 0,
      6 => 3,
      7 => 4,
      8 => 5,
      9 => 2,
      10 => 0,
    )
    

    But if you want to get rid of the original order use

    $filtered = [...array_filter(
        [0, 0, 0, 0, 1, 0, 3, 4, 5, 2, 0],
        function($value) use (&$sum) { return $sum += $value === 0 ? false : $value; }
    )];
    

    and have it like so

    array (
      0 => 1,
      1 => 0,
      2 => 3,
      3 => 4,
      4 => 5,
      5 => 2,
      6 => 0,
    )
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search