1

I have the following snippet of a large array:

Array
(
    [agreementTypes] => Array
        (
            [WS_PEAgreementType] => Array
                (
                    [0] => Array
                        (
                            [description] => Blah blah blah
                            [type] => Contract Supply
                        )

                    [1] => Array
                        (
                            [description] => Standard
                            [type] => Standard
                        )

                )

        )

Any key with "WS_PE" in it is redundant. Some are different to the above and in different levels of the array. I would like to find any key containing "WS_PE", take its values, and assign them directly to the parent of the found "WS_PE" key.

The above snippet needs to be so:

Array
    (
        [agreementTypes] => Array
            (
                [0] => Array
                    (
                        [description] => Blah blah blah
                        [type] => Contract Supply
                    )

                [1] => Array
                    (
                        [description] => Standard
                        [type] => Standard
                    )

            )

Finding the key is easy in a for loop. But I'm stuck knowing the name and level of the main array the parent of found key is (recursively).

EDIT: Here is a recursive function I have written. It works in terms of keeping track of the name of the parent key, but not the level/location in the array:

class PISupport {

    private $previousKey;

    public function stripRedundantAspireTags($rawData) {
        $returnArray = array();

        foreach($rawData as $key => $data) {

            if(false !== strpos($key, 'WS_PE')) {
                // Want to remove this key and assign data to the previous key
                $keyToUse = $this->previousKey;
            } else {
                // Just use the current key in the loop
                $keyToUse = $key;
            }

            $this->previousKey = $key;

            if(is_array($data)) {
                $obj[$keyToUse] = $this->stripRedundantAspireTags($data); //RECURSION
            } else {
                $obj[$keyToUse] = $data;
            }


        }

        return $returnArray;
    }
}

UPDATE

Almost working example thanks to didierc. The one issue is somehow it's discarding all elements but the first element of the first level of the array. Logic bug somewhere: https://gist.github.com/anonymous/b2fe834209ad74502824

Aaryn
  • 1,601
  • 2
  • 18
  • 31
  • You need to write a recursive function. – Barmar Jun 10 '14 at 21:47
  • Are the WS_PE keys fixed? I mean are they always in the second level, or might be nested? If it's nested you need to write a recursive function – Javad Jun 10 '14 at 21:48
  • 1
    See http://stackoverflow.com/questions/20592046/find-key-in-nested-associative-array/20592261#20592261 as a starting point. I don't have time right now to write a real answer. But you'll learn better if you figure it out yourself, use that as a hint. – Barmar Jun 10 '14 at 21:49
  • Here's another question that applies: http://stackoverflow.com/questions/21938442/strip-layer-from-mulitdimensional-array-php – Dan Jun 10 '14 at 22:02
  • Where is the `WS_PE` located in the key name? is it always at the beginning? If not, how do you know which key it should be assigned to in the uppper array? – didierc Jun 10 '14 at 22:04
  • Barmar - That much is clear. Javad - They can be at varying levels and it's a huge array. Technically could write code to specifically target locations but it would be a huge piece of code. Barmar - As I said, finding the key is the easy part. Dan - Thanks, but that is coded where the target is at the same level. My array isn't. – Aaryn Jun 10 '14 at 22:09

1 Answers1

1

Here's my solution: it assumes the redundant key always start with the WS_PE prefix. It also supposes that there are no such key at the first level of the array.

function strip_boxing(&$parent, &$array){
  $keys = array();
  foreach($array as $key => $value){
    if (strpos($key, 'WS_PE') === 0) {
      $parent[substr($key,5,strlen($key) - 5)] = $value;
      array_push($keys, $key);
    }
    if (is_array($value)){
      strip_boxing($array, $value);
    }
  }
  foreach($keys as $k)
    unset($array[$k]);
}

A wrapper function will do the trick for the complete array:

function remove_ws_pe(&$data){
  $of = array();
  strip_boxing($of, $data);
  $data = array_merge($of, $data);
}

This code worked with the following value:

$a = array
  (
   'agreementTypes' => array
   (
    'WS_PEagreementTypes' => array 
    (
     '0' => array(
      'description' => 'Blah blah blah',
      'type' => 'Contract Supply'
      ),
     '1' => array(
      'description' => 'Standard',
      'type' => 'Standard'
      )
     )
    ));

If you turn it into a class, you could easily tune:

  • the key matching expression (strpos...)

  • and parent key selection (substr...). Be careful with overwriting though: if you have 2 entries or more in $array which contains the "WS_PE" pattern, how do you pick which parent key will be used?

didierc
  • 14,572
  • 3
  • 32
  • 52
  • This is actually pretty clever with passing the parent array itself into the recursive call. Not sure how you would make the initial call though. You won't have a parent. I'm just trying a few things with your code now. Thank you. – Aaryn Jun 10 '14 at 22:27
  • Well, if you have these keys at the top level of the array, where would you put them anyway? – didierc Jun 10 '14 at 22:29
  • Sorry I mean I'm not sure how you would call strip_boxing() in the first place if there is no $parent. Just the original entire array. Still trying something with your code. I think this is definitely the right track. – Aaryn Jun 10 '14 at 22:37
  • it's in the answer now. – didierc Jun 10 '14 at 22:38
  • I've spent some time on this question; has been quite the brain cracker for me. In this strip_boxing function, I don't see any return. I think you somehow need to go in two levels or go recursively backwards but right now I can't think straight anymore. Good luck though. – Arie Jun 10 '14 at 23:20
  • Well, true, I chose to do a modification in place. Not sure if this would work as expected though, now that you mention it. – didierc Jun 10 '14 at 23:24
  • Still trying a few ways with this function with a bit of a hybrid of my method, storing the parent key in $this->previousKey outside the method itself so it can still recurse. Will post a solution if I crack it. – Aaryn Jun 10 '14 at 23:41
  • Thanks for the update. Very cool solution. One thing I should have noticed before is $parent[substr($key,5,strlen($key) - 5)] = $value;. This is why I am looking at storing the parent key name outside the method. Some of these "WS_PE" have no relation to the target parent. In your working example the WS_PEagreementTypes has been modified to be a lowercase a to suit it's parent, which isn't possible. Sometimes the "WS_PE" suffix is something completely different to the parent. Or slightly, as in WS_PEAgreementTypes. So trying to solve that one. But you've given me a big leg up. Thanks. – Aaryn Jun 11 '14 at 01:20
  • Yes, I had to change it because of the way I select the entry of parent to receive the moved sub-array. You could pass along with `$parent` the `$key` which must be used (it has to be a parameter or generated, since it changes from one recursion level to the next). – didierc Jun 11 '14 at 01:36
  • Sweet. I managed it with an external property. What I've found is your function actually strips all but the first element in each first level child. This is true of the original function. Probably easier to explain with a working example: https://gist.github.com/anonymous/b2fe834209ad74502824. Pretty sure this is something to do with all the pass by reference. – Aaryn Jun 11 '14 at 02:57