Home > Enterprise >  Remove previous iteration's data from re-used array
Remove previous iteration's data from re-used array

Time:04-10

I have two arrays, $array1 and $array2. $array2 is an array of arrays. For each subarray of $array2, I want to print $array1 but with additional entries that depend on whether the $array2 subarray has keys "a" or "c" with value 1. My code prints $array1 each loop, but there are additional entries in $array1 for the later iterations that I wasn't expecting. Why do I get these entries, and how do I prevent them?

Sample code:

$array1 = array(
    "service" => "coding",
    "data" => array(
        "ITEM" => array(
            array(
                "CODE" => "9999", //STANDARD
                "QUANTITY" => 1
            ),
        )
    )
);

$array2 = array(
    array(
        "a" => "1",
        "b" => "1",
        "c" => "1",
        "d" => "1",
    ),
    array(
        "cancel" => "1",
        "a" => "1",
        "b" => "",
        "c" => "",
        "d" => "1",
    ),
    array(
        "cancel" => "1",
        "a" => "",
        "b" => "1",
        "c" => "1",
        "d" => "",
    ),
);

for ($i = 0; $i < count($array2); $i  ) {
    foreach ($array2[$i] as $key => $value) {
        if($key == 'a' && $value == 1){
            array_push($array1['data']['ITEM'],
                array('SOMETHING' =>  'this_is_a',
                    'ELSE' => "1"
                )
            );
        }
        if($key == 'c' && $value == 1){
            array_push($array1['data']['ITEM'],
                array('SOMETHING' =>  'this_is_c',
                    'ELSE' => "1"
                )
            );
        }
    }

    echo "Loop #$i result:\n";    
    var_export($array1);
    echo "\n";
}

You can test the above code as a PHP Sandbox snippet.

The actual result is:

Loop #0 result:
array (
  'service' => 'coding',
  'data' => 
  array (
    'ITEM' => 
    array (
      0 => 
      array (
        'CODE' => '9999',
        'QUANTITY' => 1,
      ),
      1 => 
      array (
        'SOMETHING' => 'this_is_a',
        'ELSE' => '1',
      ),
      2 => 
      array (
        'SOMETHING' => 'this_is_c',
        'ELSE' => '1',
      ),
    ),
  ),
)
Loop #1 result:
array (
  'service' => 'coding',
  'data' => 
  array (
    'ITEM' => 
    array (
      0 => 
      array (
        'CODE' => '9999',
        'QUANTITY' => 1,
      ),
      1 => 
      array (
        'SOMETHING' => 'this_is_a',
        'ELSE' => '1',
      ),
      2 => 
      array (
        'SOMETHING' => 'this_is_c',
        'ELSE' => '1',
      ),
      3 => 
      array (
        'SOMETHING' => 'this_is_a',
        'ELSE' => '1',
      ),
    ),
  ),
)
Loop #2 result:
array (
  'service' => 'coding',
  'data' => 
  array (
    'ITEM' => 
    array (
      0 => 
      array (
        'CODE' => '9999',
        'QUANTITY' => 1,
      ),
      1 => 
      array (
        'SOMETHING' => 'this_is_a',
        'ELSE' => '1',
      ),
      2 => 
      array (
        'SOMETHING' => 'this_is_c',
        'ELSE' => '1',
      ),
      3 => 
      array (
        'SOMETHING' => 'this_is_a',
        'ELSE' => '1',
      ),
      4 => 
      array (
        'SOMETHING' => 'this_is_c',
        'ELSE' => '1',
      ),
    ),
  ),
)

The loop #0 result is correct, but the later loops have additional entries in $array1['data']['ITEM']. Desired result:

Loop #0 result:
array (
  'service' => coding
  'data' => array (
    'ITEM' => array (
      0 => array (
        'CODE' => 9999
        'QUANTITY' => 1
      )
      1 => array (
        'SOMETHING' => 'this_is_a'
        'ELSE' => 1
      )
      2 => array (
        'SOMETHING' => 'this_is_c'
        'ELSE' => 1
      )
    )
  )
)
Loop #1 result:
array (
  'service' => coding
  'data' => array (
    'ITEM' => array (
      0 => array (
        'CODE' => 9999
        'QUANTITY' => 1
      )
      1 => array (
        'SOMETHING' => 'this_is_a'
        'ELSE' => 1
      )
    )
  )
)
Loop #2 result:
array (
  'service' => coding
  'data' => array (
    'ITEM' => array (
      0 => array (
        'CODE' => 9999
        'QUANTITY' => 1
      )
      1 => array (
        'SOMETHING' => 'this_is_c'
        'ELSE' => 1
      )
    )
  )
)

CodePudding user response:

You may use array_map to loop through the second array and only push to the first array's ['data']['ITEM'] when the current iteration has a key named a and its value is 1.

$arr1 = [
    'service' => 'coding',
    'data' => [
        'ITEM' => [
            [
                'CODE' => '9999',
                'QUANTITY' => 1
            ]
        ]
    ]
];
$arr2 = [
    [
        'a' => '1',
        'b' => '1',
        'c' => '1',
        'd' => '1',
    ],
    [
        'cancel' => '1',
        'a' => '1',
        'b' => '',
        'c' => '',
        'd' => '1',
    ],
    [
        'cancel' => '1',
        'a' => '',
        'b' => '1',
        'c' => '1',
        'd' => '',
    ],
];

// loop through the second array ($arr2)
// the "use" is very important here as it let's the callback function to access $arr1 variable
$finalArr = array_map(function ($el) use ($arr1) {
    // if in the current iteration a kley named "a" and its value is "1" is found then push it to the first array's ['data']['ITEM'] key.
    // $arr1 here is passed by value so the real array won't be channged, we're, more or less, working with copy of $arr1 in the function.
    isset($el['a']) && $el['a'] == 1 && ($arr1['data']['ITEM'][] = [
        'something' => 'This is a', // tried to keep same output as yours
        'else' => $el['a'] // tried to keep same output as yours
    ]);
    // for "c" key
    isset($el['c']) && $el['c'] == 1 && ($arr1['data']['ITEM'][] = [
        'something' => 'This is c', // tried to keep same output as yours
        'else' => $el['c'] // tried to keep same output as yours
    ]);
    // return the $arr1 copy regardless of whether we pushed "a" key or not.
    return $arr1;
}, $arr2);

// print the resulting array
print_r($finalArr);

Result (for the sample data):

array(
  0 => array(
    'service' => 'coding',
    'data' => array(
      'ITEM' => array(
        0 => array(
          'CODE' => '9999',
          'QUANTITY' => 1,
        ),
        1 => array(
          'something' => 'This is a',
          'else' => '1',
        ),
        2 => array(
          'something' => 'This is c',
          'else' => '1',
        ),
      ),
    ),
  ),
  1 => array(
    'service' => 'coding',
    'data' => array(
      'ITEM' => array(
        0 => array(
          'CODE' => '9999',
          'QUANTITY' => 1,
        ),
        1 => array(
          'something' => 'This is a',
          'else' => '1',
        ),
      ),
    ),
  ),
  2 => array(
    'service' => 'coding',
    'data' => array(
      'ITEM' => array(
        0 => array(
          'CODE' => '9999',
          'QUANTITY' => 1,
        ),
        1 => array(
          'something' => 'This is c',
          'else' => '1',
        ),
      ),
    ),
  ),
)

You can learn more about callback functions (anonymous functions) in the PHP manual.

CodePudding user response:

You are not returning $array1['data']['ITEM'] to its original state between each iteration. Just write the following between your for() and your foreach().

$array1['data']['ITEM'] = array_slice($array1['data']['ITEM'], 0, 1);

Because array keys must be unique, you don't need to loop through all elements to see if a or c exists -- this will cost unnecessary cycles.

Futhermore, I find array destructuring to be a good choice to isolate the only two values you seek.

I might recommend this much simpler and more intuitive snippet: (Demo)

foreach ($array2 as $i => ['a' => $a, 'c' => $c]) {
    $temp = $array1;
    if($a == 1) {
        $temp['data']['ITEM'][] = [
            'SOMETHING' => 'this_is_a',
            'ELSE' => '1'
        ];
    }
    if ($c == 1) {
        $temp['data']['ITEM'][] = [
            'SOMETHING' => 'this_is_c',
            'ELSE' => '1'
        ];
    }

    echo "Loop #$i result:\n";    
    var_export($temp);
    echo "\n";
}

 

CodePudding user response:

The unexpected entries are present because $array1 is modified with each iteration (specifically, by array_push). To prevent this, each iteration must operate on a different copy of $array1. Since variable assignment and argument passing will each copy an array, either could be used. Similar to argument passing, closures can inherit (note: not in the OOP sense) variables from outer scopes, binding to their values (which will copy an array), providing a third potential basis for a solution.

The fix requiring the least amount of edits is to assign $array1 to another variable at the start of the loop, and replace $array1 in the rest of the loop with this variable:

for ($i = 0; $i < count($array2); $i  ) {
    $result = $array1;
    // ...

Alternatively, by moving the body of the loop to a function, $array1 becomes a copy within the function body:

function process(array $options, array $output) {
    // Ensure entries to test are present:
    $options  = ['a' => NULL, 'c' => NULL];
    /* As an alternative to the above, if entries should be added 
     * to $output whenever 'a' or 'c' has a truthy value, 
     * `! empty(...)` could be used instead of the `... == 1`
     * tests below.
     */

    if ($options['a'] == 1) {
        $output['data']['ITEM'][] = [
            'SOMETHING' =>  'this_is_a',
            'ELSE' => 1,
        ];
    }
    if ($options['c'] == 1) {
        $output['data']['ITEM'][] = [
            'SOMETHING' =>  'this_is_c',
            'ELSE' => 1,
        ];
    }
    return $output;
}

foreach ($array2 as $i => $subarray) {
    echo "// Loop #$i result:";
    var_export( process($subarray, $array1) );
    echo ";\n";
}

As a third approach, you could apply array_map to $array2, as is done by ths. For comparison, the preceding foreach loop could be replaced with a call to array_map:

var_export(
    array_map(function (array $subarray) use ($array1) {
            return process($subarray, $array1); 
        }, $array2)
);

Note that for arrays that won't be modified (or for which modification won't matter), you can pass them by reference (or bind by reference in closures) to avoid the overhead of copying them.

function process(array &$options, array $output) {
    // ...

CodePudding user response:

if(!array_key_exist($key, $array['data']['ITEM']) 
   && 
   !in_array($array['data']['ITEM'][$key] == $value)){
}
  • Related