将嵌套数组中的“点表示法”键扩展为子数组

时间:2018-07-28 16:18:19

标签: php arrays recursion adyen

我从任意深度的嵌套数组开始。在该数组中,某些键是一系列用点分隔的令牌。例如“ billingAddress.street”或“ foo.bar.baz”。我想将这些键元素扩展为数组,所以结果是所有这些键都扩展了的嵌套数组。

例如:

[
    'billingAddress.street' => 'My Street',
    'foo.bar.baz' => 'biz',
]

应扩展为:

[
    'billingAddress' => [
        'street' => 'My Street',
    ],
    'foo' => [
        'bar' => [
            'baz' => 'biz',
        ]
    ]
]

原始的“ billingAddress.street”可以与新的“ billingAddress”数组并排放置,但是不必保留(因此解决方案可以在原始数组上运行或创建新的数组)。诸如“ billingAddress.city”之类的其他元素可能需要添加到数组的同一扩展部分。

某些键可能有两个以上的令牌,这些令牌之间由点分隔,因此需要更深地扩展。

我看过array_walk_recursive(),但这仅适用于元素。对于每个匹配的元素键,我实际上想修改这些元素所在的父数组。

我看过array_map,但是它不提供对键的访问,并且据我所知不是递归的。

要扩展的示例数组:

[
    'name' => 'Name',
    'address.city' => 'City',
    'address.street' => 'Street',
    'card' => [
        'type' => 'visa',
        'details.last4' => '1234',
    ],
]

这将被扩展为:

[
    'name' => 'Name',
    'address.city' => 'City', // Optional
    'address' => [
        'city' => 'City',
        'street' => 'Street',
    ],
    'address.street' => 'Street', // Optional
    'card' => [
        'type' => 'visa',
        'details.last4' => '1234', // Optional
        'details' => [
            'last4' => '1234',
        ],
    ],
]

我认为我需要的是可以嵌套数组中的每个array并可以对其应用用户功能的东西。但是我确实怀疑我缺少明显的东西。我正在使用的支付网关使用点符号将此数组和“假装数组”的混合发送给我,我的目标是将其规范化为数组以提取部分。

我相信这个问题与SO上的类似问题有所不同,这是因为阵列和非阵列的这种混合用于扩展。从概念上讲,这是一个嵌套数组,其中需要用新数组替换 any 级别的元素的 groups 元素,因此这里发生了两个递归级别:树行走,然后展开,然后沿着展开的树走动以查看是否需要更多展开。

3 个答案:

答案 0 :(得分:5)

您可能会发现颠倒组合键(虚线)后获得的键顺序很有用。按照这种相反的顺序,将以前的结果逐步包装到新数组中会更容易,从而为一个点分键/值对创建嵌套结果。

最后,可以使用内置的//div[@class='form-box-inner'] 函数将部分结果合并为累积的“大”结果:

array_merge_recursive

看到它在repl.it上运行

答案 1 :(得分:4)

这是递归尝试。请注意,这不会删除旧键,不维护任何键顺序,并且会忽略类型为foo.bar.baz的键。

function expand(&$data) {
  if (is_array($data)) {
    foreach ($data as $k => $v) {
      $e = explode(".", $k);

      if (count($e) == 2) {
        [$a, $b] = $e;
        $data[$a][$b]= $v;
      }

      expand($data[$k]);
    }
  }
}

结果:

Array
(
    [name] => Name
    [address.city] => City
    [address.street] => Street
    [card] => Array
        (
            [type] => visa
            [details.last4] => 1234
            [details] => Array
                (
                    [last4] => 1234
                )

        )

    [address] => Array
        (
            [city] => City
            [street] => Street
        )

)

说明:

在函数的任何调用中,如果参数为数组,则遍历键和值以查找其中带有.的键。对于任何此类密钥,请将其展开。在数组中的所有键上递归调用此函数。

完整版本:

这是一个完整版本,它支持多个.并随后清理密钥:

function expand(&$data) {
  if (is_array($data)) {
    foreach ($data as $k => $v) {
      $e = explode(".", $k);
      $a = array_shift($e);

      if (count($e) == 1) {
        $data[$a][$e[0]] = $v;
      }
      else if (count($e) > 1) {
        $data[$a][implode(".", $e)] = $v;
      }
    }

    foreach ($data as $k => $v) {
      expand($data[$k]);

      if (preg_match('/\./', $k)) {
        unset($data[$k]);
      }
    }
  }
}

这里是repl

答案 2 :(得分:2)

@trincot的另一个解决方案被认为更优雅,它是我现在正在使用的解决方案。

这是我的解决方案,它扩展了@ggorlen给出的解决方案和技巧

我采用的方法是:

  • 创建一个新数组,而不是对初始数组进行操作。
  • 无需保留旧的预展开元素。如果需要,可以轻松添加它们。
  • 从根数组一次一次扩展键,其余的递归地递回。

类方法:

protected function expandKeys($arr)
{
    $result = [];

    while (count($arr)) {
        // Shift the first element off the array - both key and value.
        // We are treating this like a stack of elements to work through,
        // and some new elements may be added to the stack as we go.

        $value = reset($arr);
        $key = key($arr);
        unset($arr[$key]);

        if (strpos($key, '.') !== false) {
            list($base, $ext) = explode('.', $key, 2);

            if (! array_key_exists($base, $arr)) {
                // This will be another array element on the end of the
                // arr stack, to recurse into.

                $arr[$base] = [];
            }

            // Add the value nested one level in.
            // Value at $arr['bar.baz.biz'] is now at $arr['bar']['baz.biz']
            // We may also add to this element before we get to processing it,
            // for example $arr['bar.baz.bam']


            $arr[$base][$ext] = $value;
        } elseif (is_array($value)) {
            // We already have an array value, so give the value
            // the same treatment in case any keys need expanding further.

            $result[$key] = $this->expandKeys($value);
        } else {
            // A scalar value with no expandable key.

            $result[$key] = $value;
        }
    }

    return $result;
}

$result = $this->expandKeys($sourceArray)
相关问题