SkyRar
SkyRar

Reputation: 1277

Array recursive replace based on a condition for another key?

I have one dynamic multidimensional-array having unknown depth. I wanted to replace a key=>value based on another key=>value condition.

E.g replace $settings[$cient_id]'s value when $form_id is 1f44537.

$data = array (
  0 => 
  array (
    'id' => '2cd5985',
    'elType' => 'section',
    'settings' => array(),
    'elements' => array (
      0 => array (
        'id' => '88063e6',
        'elType' => 'column',
        'settings' => array(),
        'elements' => array (
          0 => 
          array (
            'id' => '1f44537',
            'elType' => 'widget',
            'settings' => array (
              'ap_google_sheet_client_id' => 'test_id',
            ),
            'elements' => array (
            ),
            'widgetType' => 'form',
          ),
        )
      ),
      1 => array (
        'id' => '7878c73',
        'elType' => 'column',
        'settings' => array(),
        'elements' => array (
          0 => 
          array (
            'id' => '1f44537',
            'elType' => 'widget',
            'settings' => array (
              'ap_google_sheet_client_id' => 'test_id',
            ),
            'elements' => array (
            ),
            'widgetType' => 'form',
          ),
        )
      ),
    ),
  ),
);

Here are my unsuccessful tries

Try-1

public function replace_recursive( $elements, $form_id ) {
        foreach ( $elements as &$element) {

            if ( $element['id'] === $form_id ) {
                $element['settings']['ap_google_sheet_client_id'] = 'replaced';
                return $elements;
            }

            if ( ! empty( $element['elements'] ) ) {
                $elements = self::replace_recursive( $element['elements'], $form_id );

                if ( $elements ) {
                    return $elements;
                }
            }

        }
    }

Try-2

This solved the issue. But the loop doesn't stop when found the element. It loops through all the elements.

public function replace_recursive( $elements, $form_id ) {

        foreach ( $elements as &$element) {

            if ( $element['id'] === $form_id ){
                $element['settings']['ap_google_sheet_client_id'] = 'replaced';
                write_log('A');
                break;
            }

            if ( ! empty( $element['elements'] ) ) {
                self::replace_recursive( $element['elements'], $form_id );
                write_log('B');
            }
        }

        return $elements;
}

Value replaced but I am not getting the full array $elements. Rather than getting the child array for which the value was replaced.

Try-3

Facing the same issue again. The named keys that are on top of elType=widget are converted to numbered keys. Here is a sandbox.

https://sandbox.onlinephpfunctions.com/code/530fb53612dd7a2017b236f218665c0142f1e63f

Upvotes: 0

Views: 173

Answers (1)

trincot
trincot

Reputation: 350300

As you state, the code is replacing the value correctly. It is just that you are currently returning the value $elements as it is returned from the recursive call. But that is a smaller array than the overall array, so you should not bubble that array further up.

Instead, just inject that result in the current wrapping array at the "elements" key, and return the resulting array:

$data = [[
    'id' => '2cd5985',
    'elements' => [[
        'id' => '88063e6',
        'elements' => [[
            'id' => '1f44537',
            'settings' => [
                'form_name' => 'New Form',
                'form_fields' => [[
                    'custom_id' => 'name',
                    'field_label' => 'Name',
                    'placeholder' => 'Name',
                    '_id' => '948d900',
                    'field_pattern_message' => 'Please match the requested format',
                ],
                [
                    'custom_id' => 'email',
                    'field_type' => 'email',
                    'required' => 'true',
                    'field_label' => 'Email',
                    'placeholder' => 'Email',
                    '_id' => '243a095',
                    'field_pattern_message' => 'Please match the requested format',
                ]],
                'ap_google_sheet_client_id' => '',
            ],
            'elements' => [],
            'widgetType' => 'form',
        ]],
        'isInner' => false,
    ]],
    'isInner' => false,
]];

function replace_recursive( $elements, $form_id ) {
    foreach ( $elements as &$element) {
        if ( $element['id'] === $form_id ) {
            $element['settings']['ap_google_sheet_client_id'] = 'replaced';
            return $elements;
        }

        if ( ! empty( $element['elements'] ) ) {
            $subelements = replace_recursive( $element['elements'], $form_id );
            if ( $subelements ) { 
                // Inject result back into our array
                $element["elements"] = $subelements; 
                return $elements;
            }
        }
    }
}
    
$res = replace_recursive( $data, "1f44537");
var_dump($res);

See it with another input example in this sandbox

In the second try, you place the return outside of the loop. But this means you always return the array. Yet, you need somehow an indication whether the replacement happened or not. It is probably better to return null when you didn't replace the value, and only return the full array when there was a replacement. Otherwise you have no indication when you can stop looking further.

Upvotes: 1

Related Questions