rfc:array_delete

Request for Comments: array_delete() for elements deletion

Introduction

This RFC proposes the addition of a set of two new functions that simplify working with arrays containing a set of unique values or objects that do not have any natural (or distinct) scalar keys that can be used as indices.

While the array-type in PHP does not support set-semantics, these functions simplify working with a set of unique values or objects stored in an array.

array_delete

The first function simplifies removal of a value from an array, when the index is not known:

int array_delete(&$array, $value, $strict = TRUE)

The function is “destructive”, in the sense that it modifies the array in-place - for non-destructive removal, use array_filter().

The int return-value indicates number of removed elements.

To clarify how this function works, here is the PHP equivalent function:

function array_delete(&$array, $value, $strict = TRUE) {
    $count = 0;
    if ($strict) {
        foreach ($array as $key => $item) {
            if ($item === $value) {
                $count++;
                unset($array[$key]);
            }
        }
    } else {
        foreach ($array as $key => $item) {
            if ($item == $value) {
                $count++;
                unset($array[$key]);
            }
        }
    }
    return $count;    
}

If the same value occurs more than once in the given array, all occurrences of the given value will be deleted.

To prevent accidental deletion, $strict defaults to true - note that this differs from array_search() where the most likely objective is to “find something”.

Above example is inefficient, old PHP users should use array_walk().

function array_delete(&$array, $value, $strict = TRUE) {
    $count = 0;
    array_walk($array, function($item, $key) use (&$array, &$count, $value, $strict) {
        if ($strict) {
            if ($item === $value) {
                $count++;
                unset($array[$key]);
            }
        } else {
            if ($item == $value) {
                $count++;
                unset($array[$key]);
            }
        }
    };
    return $count;    
}

Note for non internal programmers: array_walk() is not fool safe function. It will cause unwanted behavior if one reorder elements or delete elements to be processed next. Above example works as expected. i.e. Deleting currently working element is normal operation with zend hash.

int array_delete_recursive(&$array, $value, $strict = TRUE)

Recursive variant.

array_udelete

int array_udelete(&$array, callable $callable)

$callable takes 2 parameters, ($value [, $key]) When callable returns true, element is deleted.

Use cases of array_udelete()

// Delete values < 100
array_udelete($array, function($v) {return $v < 100;});
// Delete values <== 100
array_udelete($array, function($v) {return $v <== 100;});
// Delete values non scalar
array_udelete($array, function($v) {return !is_scalar($v);});
// Delete object values
array_udelete($array, function($v) {return is_object($v);});
// Delete values more complex operation. e.g. lookup DB, etc.
array_udelete($array, function($v) {return myCondition($v);});
// Delete array if array element sum > 100
array_udelete($array, function($v) {return array_sum($v) > 100;});
 
// Delete values with respect to key and value
array_udelete($array, function($v, $k) {return $v*$k > 1000;});
// Delete odd keys
array_udelete($array, function($v, $k) {return $k % 2;});
// Delete hash(i.e. string key)
array_udelete($array, function($v, $k) {return is_string($k);});
 
// Delete values with user supplied $value
array_udelete($array, function($v) use ($value) {return $v === $value;});
int array_udelete_recursive(&$array, callable $callable)

Recursive variant.

array_add

To complement the array_delete() function, in terms of working with a set of unique values, a second function is proposed:

int array_add(&$array, $value, $strict = TRUE)

This function is “destructive”, like it's counterpart - for non-destructive addition, use array_merge().

The boolean return-value indicates whether or not the specified value was not already present and was added.

To clarify how this function works, here is the PHP equivalent function:

function array_add(&$array, $value, $strict = TRUE) {
  if (false === array_search($value, $array, $strict)) {
    $array[] = $value;
    return true;
  }
  return false;
}

To prevent accidentally adding duplicates, $strict defaults to true - this is consistent with array_delete().

Criticism

These functions allow you to have set-like behavior but without performance benefits traditionally associated with a set.

The array_udelete function is practically identical to array_filter. The difference is that array_udelete would modify the function in-place and array_filter would not. Is it really that hard/unnecessary to reassign the result of array_filter back to the array?

Answer to criticism

If user would like to delete elements, they should use array_walk() rather than array_filter() as it does not delete elements, but creates new array. i.e. Memory and execution is inefficient. Stack overflow and internals thread shows users are not able to choose right API for element deletion. Therefore, array_udelete() is worth to have.

Using array_walk to delete elements is undefined behavior

It says so right in the manual (look under funcname's description). There is not currently a defined function that would allow you to delete elements from the array like this, hence the array_delete proposal.

Please add this

I was needing something exactly like this!

Deleting CURRENTLY working element is OK

If you are module or core programmer, you should know deleting CURRENTLY working element will not cause any problems as it is NORMAL operation for zend hash. Please ask if you don't understand what we are talking about before edit RFC.

If one reorder element or delete next element to be processed, it will case unwanted behavior. PHP is programming language so shooting your own foot is free for users.

However, this brings up good reason to introduce array_udelete(). array_walk() is certainly not a fool safe function while array_udelete() is.

Proposal and Patch

No patch yet.

Reference

Changelog

  • 0.1 Initial version 2012/08/21
  • 0.2 Changed to Rasmus' gist version 2012/08/21
  • 0.3 Changed to array_delete() to have callable and return int. Added array_delete_recursive().
  • 0.4 Reverted callable changes.
  • 0.5 Add array_udelete()
  • 0.6 Removed anything with `array_walk`. Removing things with array_walk is undefined. Stop putting it back in, please!
rfc/array_delete.txt · Last modified: 2014/04/09 00:56 by levim