Search code examples
phpmultidimensional-arrayfilteringwhitelistarray-intersect

Removing values from multidimensional array based on multidimentional whitelist


I have the following white list:

private $conditionalFieldsWhitelist = [
    'Basic Fields' => [
        'contact_name',
        'contact_lastname',
    ],
    'Required Fields' => [
        'some_required_field',
    ]
];

That I want to run agains an array that looks like this:

$myArray = [
    'Basic Fields' => [
        [
            'field_name' => 'contact_name',
            'field_readable' => $this->language->get('First Name'),
            'field_type' => 'string',
            'field_required' => 'no',
            'field_placeholder' => $this->language->get('Type your first name')
        ], [
            'field_name' => 'contact_lastname',
            'field_readable' => $this->language->get('Last Name'),
            'field_type' => 'string',
            'field_required' => 'no',
            'field_placeholder' => $this->language->get('Type your last name')
        ], [
            'field_name' => 'contact_email',
            'field_readable' => $this->language->get('Email Address'),
            'field_type' => 'string',
            'field_required' => 'yes',
            'field_placeholder' => '[email protected]'
        ], [
            'field_name' => 'contact_mobile',
            'field_readable' => $this->language->get('Mobile Number'),
            'field_type' => 'string',
            'field_required' => 'yes',
            'field_placeholder' => '+27881234567'
        ]
    ],
    'Required Fields' => [
        [
            'field_name' => 'some_required_field',
            'field_readable' => $this->language->get('Required Field'),
            'field_type' => 'string',
            'field_required' => 'no',
        ],
    ],
    'This Should Be Removed' => [
        [
            'field_name' => 'not_needed_field',
            'field_readable' => $this->language->get('Required Field'),
            'field_type' => 'string',
            'field_required' => 'no',
        ]
    ]
];

Obviously this are watered down versions of the actual arrays.

My code to do this looks like so:

public function getConditionalFields()
{
    $conditionalFields = $this->formFieldGroupingsViewHelper->getGroupedFields();
    foreach ($conditionalFields as $group => $fields) {
        if (in_array($group, array_keys($this->conditionalFieldsWhitelist)) === false) {
            unset($conditionalFields[$group]);
            continue;
        }
        foreach ($fields as $index => $field) {
            if (in_array($field['field_name'], $this->conditionalFieldsWhitelist[$group]) === false) {
                unset($conditionalFields[$group][$index]);
                continue;
            }
        }
        $conditionalFields[$group] = array_values($conditionalFields[$group]);
    }
    return $conditionalFields;
}

This, however, doesn't seem like it's very clean and makes use of the power of PHP.

Is there a simpler, better, neater way to do this? Something like a recursive array_intersect that works on array keys as well.

This is the outcome that I expect:

[
    'Basic Fields' => [
        [
            'field_name' => 'contact_name',
            'field_readable' => $this->language->get('First Name'),
            'field_type' => 'string',
            'field_required' => 'no',
            'field_placeholder' => $this->language->get('Type your first name')
        ], [
            'field_name' => 'contact_lastname',
            'field_readable' => $this->language->get('Last Name'),
            'field_type' => 'string',
            'field_required' => 'no',
            'field_placeholder' => $this->language->get('Type your last name')
        ]
    ],
    'Required Fields' => [
        [
            'field_name' => 'some_required_field',
            'field_readable' => $this->language->get('Required Field'),
            'field_type' => 'string',
            'field_required' => 'no',
        ],
    ]
]

Solution

  • I don't know if this is "better". It is different.

    Code: (Demo)

    $whitegroups = array_keys($conditionalFieldsWhitelist); // only necessary if multiple groups are possible in your project
    
    foreach ($myArray as $group => $field_sets) {
        if (in_array($group, $whitegroups)) {
            $whitesubsets = $conditionalFieldsWhitelist[$group];  // only filter subsets that pass the group check
            $result[$group] = array_filter($field_sets, function($set) use ($whitesubsets) {
                return in_array($set['field_name'],$whitesubsets);  // filter the subsets
            });
        }
    }
    
    var_export($result);
    

    Or: (with array_intersect_key())

    $myArray = array_intersect_key($myArray, $conditionalFieldsWhitelist);  // filter the groups/keys
    foreach ($myArray as $group => $field_sets) {
        $whitesubsets = $conditionalFieldsWhitelist[$group];
        $result[$group] = array_filter($field_sets, function($set) use ($whitesubsets) {
            return in_array($set['field_name'],$whitesubsets);  // filter the subsets
        });
    }
    var_export($result);
    

    Or (with array_intersect_key() and modifying by reference):

    $myArray = array_intersect_key($myArray, $conditionalFieldsWhitelist);
    foreach ($myArray as $group => &$field_sets) {   // modify by reference
        $whitesubsets = $conditionalFieldsWhitelist[$group];
        $field_sets = array_filter($field_sets, function($set) use ($whitesubsets) {
            return in_array($set['field_name'],$whitesubsets);
        });
    }
    var_export($myArray);
    

    Output:

    array (
      'Basic Fields' => 
      array (
        0 => 
        array (
          'field_name' => 'contact_name',
          'field_type' => 'string',
          'field_required' => 'no',
        ),
        1 => 
        array (
          'field_name' => 'contact_lastname',
          'field_type' => 'string',
          'field_required' => 'no',
        ),
      ),
    )