0

I need to remove an element form a deeply nested array of unknown structure (i.e. I do not know what the key sequence would be to address the element in order to unset it). The element I am removing however does have a consistent structure (stdObject), so I can search the entire multidimensional array to find it, but then it must be removed. Thoughts on how to accomplish this?

EDIT: This is the function I have right now trying to achieve this.

function _subqueue_filter_reference(&$where)
{
    foreach ($where as $key => $value) {
        if (is_array($value))
        {
            foreach ($value as $filter_key => $filter)
            {
                if (isset($filter['field']) && is_string($filter['field']) && $filter['field'] == 'nodequeue_nodes_node__nodequeue_subqueue.reference')
                {
                    unset($value[$filter_key]);
                    return TRUE;
                }
            }
            return _subqueue_filter_reference($value);
        }
    }
    return FALSE;
}

EDIT #2: Snipped of array structure from var_dump.

array (size=1)
  1 => 
    array (size=3)
      'conditions' => 
        array (size=5)
          0 => 
            array (size=3)
              ...
          1 => 
            array (size=3)
              ...
          2 => 
            array (size=3)
              ...
          3 => 
            array (size=3)
              ...
          4 => 
            array (size=3)
              ...
      'args' => 
        array (size=0)
          empty
      'type' => string 'AND' (length=3)

...so assuming that this entire structure is assigned to $array, the element I need to remove is $array[1]['conditions'][4] where that target is an array with three fields:

  • field
  • value
  • operator

...all of which are string values.

9
  • stackoverflow.com/questions/369602/… Commented Aug 20, 2013 at 13:10
  • I've already read this and it does not answer my question. Using unset would be easy if I knew the exact key sequence to the deeply nested element, but I don't. Commented Aug 20, 2013 at 13:12
  • You know that you have to search it. So when you found it use unset. Commented Aug 20, 2013 at 13:13
  • php.net/manual/en/function.array-search.php Commented Aug 20, 2013 at 13:14
  • Maybe I don't understand. Search and remove...right? Commented Aug 20, 2013 at 13:14

3 Answers 3

2

This is just a cursor problem.

function recursive_unset(&$array)
{
    foreach ($array as $key => &$value) # See the added & here.
    {
        if(is_array($value))
        {
            if(isset($value['field']) && $value['field'] == 'nodequeue_nodes_node__nodequeue_subqueue.reference')
            {
                unset($array[$key]);
            }
            recursive_unset($value);
        }
    }
}

Notes : you don't need to use is_string here, you can just make the comparison as you're comparing to a string and the value exists.

Don't use return unless you're sure there is only one occurrence of your value.

Edit :

Here is a complete example with an array similar to what you showed :

$test = array (
        1 => array (
                'conditions' =>
                array (
                        0 => array ('field' => 'dont_care1', 'value' => 'test', 'operator' => 'whatever'),
                        1 => array ('field' => 'dont_care2', 'value' => 'test', 'operator' => 'whatever'),
                        2 => array ('field' => 'nodequeue_nodes_node__nodequeue_subqueue.reference', 'value' => 'test', 'operator' => 'whatever'),
                        3 => array ('field' => 'dont_care3', 'value' => 'test', 'operator' => 'whatever')
                ),
        'args' => array (),
        'type' => 'AND'
));

var_dump($test);

function recursive_unset(&$array)
{
    foreach ($array as $key => &$value)
    {
        if(is_array($value))
        {
            if(isset($value['field']) && $value['field'] == 'nodequeue_nodes_node__nodequeue_subqueue.reference')
            {
                unset($array[$key]);
            }
            recursive_unset($value);
        }
    }
}

recursive_unset($test);

var_dump($test);
Sign up to request clarification or add additional context in comments.

3 Comments

Just tested this code on a 3 dimensions array and it worked. It wouldn't work if the 'field' was on the first level of the array though (won't delete the array itself).
I added a sample of the array structure that I am searching.
Edited to show complete code. Still works for me with the sampled array. Maybe show your implementation of the code you made ? Will be a better solution than an EVAL.
0

One way to solve this was to extend your recursive function with a second parameter:

function _subqueue_filter_reference(&$where, $keyPath = array())

You'd still do the initial call the same way, but the internal call to itself would be this:

return _subqueue_filter_reference($value, array_merge($keyPath, array($key)));

This would provide you with the full path of keys to reach the current part of the array in the $keyPath variable. You can then use this in your unset. If you're feeling really dirty, you might even use eval for this as a valid shortcut, since the source of the input you'd give it would be fully within your control.

Edit: On another note, it may not be a good idea to delete items from the array while you're looping over it. I'm not sure how a foreach compiles but if you get weird errors you may want to separate your finding logic from the deleting logic.

Comments

0

I have arrived at a solution that is a spin-off of the function found at http://www.php.net/manual/en/function.array-search.php#79535 (array_search documentation).

Code:

function _subqueue_filter_reference($haystack,&$tree=array(),$index="")
{
    // dpm($haystack);
    if (is_array($haystack))
    {

        $result = array();

        if (count($tree)==0)
        {
            $tree = array() + $haystack;
        }

        foreach($haystack as $k=>$current)
        {
            if (is_array($current))
            {
                if (isset($current['field']) && is_string($current['field']) && $current['field'] == 'nodequeue_nodes_node__nodequeue_subqueue.reference')
                {
                    eval("unset(\$tree{$index}[{$k}]);"); // unset all elements = empty array
                }
                _subqueue_filter_reference($current,$tree,$index."[$k]");
            }
        } 
    } 
    return $tree;
}

I hate having to use eval as it SCREAMS of a giant, gaping security hole, but it's pretty secure and the values being called in eval are generated explicitly by Drupal core and Views. I'm okay with using it for now.

Anyway, when I return the tree I simply replace the old array with the newly returned tree array. Works like a charm.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.