7

I have some default configurations, and some specific configurations which would be configurable. I need to merge the specific configurations into the default configurations.

  • In the case that the specific config option does not exist, the default option will be used.
  • In the case that the value is a scalar, the specific configuration should be applied
  • In the case that the value is a scalar array, the arrays should be merged and array_unique applied.
  • In the case that the value is an associative array, We need to apply the above scalar and scalar_array rules.

Example:

$defaultConfigs = [
   'scalar1' => 1,
   'scalar2' => "Apple",
   'array_scalar' => [3,4,5],
   'array_associative' => [
      'scalar' => 1,
      'array_scalar' => [1,2,3],
      'array_associative' => [
          ...
      ]
   ],
];

$specificConfigs = [
   'scalar1' => "A",                          
   'array_scalar' => [3,4,5],   
   'array_associative' => [
      'scalar' => 1,
      'array_scalar' => [1,2,3],
      'array_associative' => [
          ...
      ]
   ],
];

Expected Output:

$expectedConfigs = [
   'scalar1' => "A",                  // Overridden
   'scalar2' => "Apple",              // Default used
   'array_scalar' => [1,2,3,4,5],     // Scalar merged and array_unique
   'array_associative' => [
      'scalar' => "B",                // Overridden
      'array_scalar' => [1,2,3,4,5],  // Scalar merged and array_unique
      'array_associative' => [
          ...
      ]
   ],
];

Is there a nice clean way of achieving this?

Gravy
  • 12,264
  • 26
  • 124
  • 193
  • This is interesting, if I understood, you are trying something like SublimeText configuration files (just to make it clear)? – Asur Feb 22 '16 at 11:45
  • I think you understood it exactly – Gravy Feb 22 '16 at 11:51
  • I would go with a custom recursive function, because you might need more features than the php recursive functions can have. But anyway I personally find it completly ok. – Asur Feb 22 '16 at 12:00

3 Answers3

4

This function obtains your desired result. It assume that specific types are coherents with default types, so no coherence check is performed. The function iterate specific configuration array and check corresponding default value1: if it is scalar, replace default value; if it is a enumerated array2, it merge unique values; otherwise function call itself with current values as arguments.

function fillConfig( $default, $specific )
{
    foreach( $specific as $key=> $val )
    {
        if( isset( $default[$key] ) )
        {
            if( ! is_array( $default[$key] ) )
            {
                $default[$key] = $val;
            }
            elseif( array_keys($default[$key]) === range(0, count($default[$key]) - 1) )
            {
                $default[$key] = array_unique( array_merge( $default[$key], $val ) );
            }
            else
            {
                $default[$key] = fillConfig( $default[$key], $val );
            }
        }
        else
        {
            // This happens when a specific key doesn't exists in default configuration.
            // I think that in this case the value must be omitted,
            // otherwise you can un-comment following line:
            // $default[$key] = $val;
        }
    }
    return $default;
}

Calling the function in this way:

$result = fillConfig( $defaultConfigs, $specificConfigs );

$result, applied to your arrays sample, is this:

Array
(
    [scalar1] => A
    [scalar2] => Apple
    [array_scalar] => Array
        (
            [0] => 3
            [1] => 4
            [2] => 5
        )
    [array_associative] => Array
        (
            [scalar] => 1
            [array_scalar] => Array
                (
                    [0] => 1
                    [1] => 2
                    [2] => 3
                )
            [array_associative] => Array
                (
                )
        )
)

With this array couple:

$defaultConfigs = [
   'scalar1' => 1,
   'scalar2' => "Apple",
   'array_scalar' => [3,4,5],
   'array_associative' => [
      'scalar' => 1,
      'array_scalar' => [1,2,3],
      'array_associative' => [

      ]
   ],
];

$specificConfigs = [
   'scalar1' => "A",                          
   'array_scalar' => [3,4,5],   
   'array_associative' => [
      'scalar' => B,
      'array_scalar' => [3,4,5],
      'array_associative' => [

      ]
   ],
];

$result is:

Array
(
    [scalar1] => A
    [scalar2] => Apple
    [array_scalar] => Array
        (
            [0] => 3
            [1] => 4
            [2] => 5
        )

    [array_associative] => Array
        (
            [scalar] => B
            [array_scalar] => Array
                (
                    [0] => 1
                    [1] => 2
                    [2] => 3
                    [4] => 4
                    [5] => 5
                )

            [array_associative] => Array
                (
                )

        )

)

Notes:

1 Yes, this is a bit incoherent: I felt was better iterate through specific array (not existent items remain untouched), but performing value check on default array, that is the reference point.

2 The enumerated/associative array check is based on this answer.

Community
  • 1
  • 1
fusion3k
  • 11,568
  • 4
  • 25
  • 47
1

My case was slightly different but it might be of help. I needed to replace scalars and array_merge_recursive on arrays.

class ArrayUtil {

    public static function mergeRecursive(array $array1, $array2) {
        if($array2 && is_array($array2)) {
            foreach($array2 as $key => $val2) {
                if (is_array($val2) && (null!==($val1 = isset($array1[$key]) ? $array1[$key] : null)) && is_array($val1)) {
                    $array1[$key] = self::mergeRecursive($val1,$val2);
                } else {
                    $array1[$key] = $val2;
                }
            }
        }
        return $array1;
    }
}
Niclas
  • 1,306
  • 5
  • 15
  • 28
0

I rewrote function from the first answer a bit for using with array of configs:

private function mergeConfigs(array $configs): array
{
    $default = array_shift($configs);
    return array_reduce($configs, function (array $result, array $config) {
        foreach ($config as $key => $val) {
            if (!isset($result[$key]) || !is_array($result[$key])) {
                $result[$key] = $val;
                continue;
            }
            $result[$key] = array_keys($result[$key]) === range(0, count($result[$key]) - 1)
                ? array_unique(array_merge($result[$key], $val))
                : $this->mergeConfigs([$result[$key], $val]);
        }
        return $result;
    }, $default);
}
Nikita Leshchev
  • 1,784
  • 2
  • 14
  • 26