PHP在连接键时将嵌套数组转换为单个数组?
Posted
技术标签:
【中文标题】PHP在连接键时将嵌套数组转换为单个数组?【英文标题】:PHP convert nested array to single array while concatenating keys? 【发布时间】:2010-12-25 08:52:47 【问题描述】:这是一个示例数组:
$foo = array(
'employer' => array(
'name' => 'Foobar Inc',
'phone' => '555-555-5555'
),
'employee' => array(
'name' => 'John Doe',
'phone' => '555-555-5556',
'address' => array(
'state' => 'California',
'zip' => '90210'
)
),
'modified' => '2009-12-01',
);
我想得到这样的结果:
$fooCompressed = array(
'employer_name' => 'Foobar Inc',
'employer_phone' => '555-555-5555',
'employee_name' => 'John Doe',
'employee_phone' => '555-555-5556'
'employee_address_state' => 'California',
'employee_address_zip' => '90210',
'modified' => '2009-12-01'
)
我将如何编写一个递归函数来处理这个问题?
【问题讨论】:
【参考方案1】:类似这样的:
function makeNonNestedRecursive(array &$out, $key, array $in)
foreach($in as $k=>$v)
if(is_array($v))
makeNonNestedRecursive($out, $key . $k . '_', $v);
else
$out[$key . $k] = $v;
function makeNonNested(array $in)
$out = array();
makeNonNestedRecursive($out, '', $in);
return $out;
// Example
$fooCompressed = makeNonNested($foo);
【讨论】:
+1 这与我所做的非常接近。因为键正在被修改,所以没有内置函数可以为你做这件事,你肯定需要递归来深入了解任何也是数组的子值。【参考方案2】:我认为这个使用 http_build_query 的“技巧”在没有递归的情况下不那么令人眼花缭乱(或者至少让 php 为你做这件事)
如果您的 str_replace 使用 [ 和 ] 的 url 编码值,则为 3 行代码
$string = http_build_query($array);
$string = urldecode($string);
$string = str_replace(
array('[',']'),
array('_','') ,
$string
);
parse_str($string, $flat_array);
$flat_array 变为:
array(7)
["employer_name"] =>"Foobar Inc"
["employer_phone"] =>"555-555-5555"
["employee_name"] =>"John Doe"
["employee_phone"] =>"555-555-5556"
["employee_address_state"]=>"California"
["employee_address_zip"] =>"90210"
["modified"] =>"2009-12-01"
【讨论】:
【参考方案3】:这是一个允许您通过第二个参数指定***前缀的函数:
function flatten_array($array, $prefix = null)
if ($prefix) $prefix .= '_';
$items = array();
foreach ($array as $key => $value)
if (is_array($value))
$items = array_merge($items, flatten_array($value, $prefix . $key));
else
$items[$prefix . $key] = $value;
return $items;
【讨论】:
【参考方案4】:我更喜欢的方法与此处发布的一些方法非常相似,但并不相同。我在一个受骗的帖子中发现了它:https://***.com/a/9546215/4791386 用户“Felix Kling”
他的代码将数组键展平,生成带有点连接键的一维数组,这意味着数值数组将创建自己的“键路径”。这非常有用,但是数组中的大量相似项可能会导致大量无意义的相似路径。
function flatten($array, $prefix = '')
$result = array();
foreach($array as $key=>$value)
if(is_array($value))
$result = $result + flatten($value, $prefix . $key . '.');
else
$result[$prefix . $key] = $value;
return $result;
在我的例子中,我还需要一个“独特的”路径作为数组键,以及我可以指定的数据样本。所以我扩展了他的方法,添加了一个数字键压缩可选参数。还添加了可选的参数分隔符配置。
主要目的是便于分析关键结构和路径相关数据。我认为当预期任务是键映射以进行进一步的完整数据操作时,此方法很有用。
/**
* Convert a multidimensional array into a single dimension array.
* Nested array keys will be concatenated with the $separator string
* Numeric keys can also be flattened in a "unique key" array style with $numeric_squash
* If $numeric_squash is true, numeric array keys are concatenated with $numeric_squash_separator,
* for later detection and processing if necessary. "[*]" by default.
* If $numeric_squash_separator is set to false, the array key is flattened so that the values
* would be displayed as if there were no numeric array.
*
* array $array : Array to be flattened
* string $prefix : String to prepend on flattened keys
* string $separator : String concatenated between nested array keys.
* bool $numeric_squash : Squash numeric array keys
* string $numeric_squash_separator : String replacing numeric keys, none if false
*/
public static function array_flatten($array, $prefix = '', $separator = '.' , $numeric_squash = false , $numeric_squash_separator = '[*]')
$result = array();
foreach($array as $key => $value)
if(is_array($value))
if($numeric_squash && is_numeric($key))
$n_key = $numeric_squash_separator ? $numeric_squash_separator . $separator: '';
else
$n_key = $key . $separator;
$result = $result + self::array_flatten($value, $prefix . $n_key , $separator , $numeric_squash , $numeric_squash_separator);
else
$result[$prefix . ($numeric_squash && is_numeric($key) ? '' : $key)] = $value;
return $result;
另外说这个函数没有性能优化,迭代可以保存在 numeric_squash 上,也可以保存一些比较操作。
【讨论】:
【参考方案5】:只有 array_* php 函数 + 递归的解决方案:
<?php
$array = array(
"level1"=>"value",
"level2" => ["level11" => "value", "level21" => "value"],
"level3" => ["level2" => ["level1" => "value"]],
"level4" => ["level3" => ["level2" => ["level1" => "value"]]],
"level5" => ["level4" => ["level3" => ["level2" => ["level1" => "value"]]]],
);
class GharbiFlat
/**
* flatten array with combined keys
*/
public function arrayFlat($array, $keySeparator = '_')
$result = [];
array_walk(
$array,
function ($v, $pk) use (&$result, $keySeparator)
if (is_array($v))
$result += $this->arrayFlat(
array_combine(
array_map(
function ($k) use ($pk, $keySeparator)
return $pk . $keySeparator . $k;
,
array_keys($v)
),
$v
),
$keySeparator
);
else
$result[$pk] = $v;
);
return $result;
$example = new GharbiFlat();
print_r($example->arrayFlat($array));
输出:
Array
(
[level1] => value
[level2_level11] => value
[level2_level21] => value
[level3_level2_level1] => value
[level4_level3_level2_level1] => value
[level5_level4_level3_level2_level1] => value
)
【讨论】:
【参考方案6】:/**
* Flatten a multi-dimensional array or a nested object, constructing concatenated keys for
* nested elements.
* @param array or object $array - the array or object to be flattened
* @param array or string $key_path - current parent keys path.
* Pass this parameter as string if you need to set a common prefix for all keys
* @param string $level_separator - keys concatenation glue
* @param array $flat - resulting flattened array (omit this parameter when calling the function)
* @return single-dimensional array with all array keys as concatenated keys of elements'
* paths through the data structure
*/
function flattenArray($array, &$key_path = array(), $level_separator = '.', &$flat = array())
if(!is_array($key_path))
// sanitize key_path
$key_path = array((string)$key_path);
foreach($array as $key => $value)
// push current key to path
array_push($key_path, $key);
if(is_array($value) || is_object($value))
// next level recursion
$flat = array_merge($flat, flattenArray($value, $key_path, $level_separator, $flat));
else
// write the value directly
$flat[implode($level_separator, $key_path)] = $value;
// remove used key
array_pop($key_path);
return $flat;
【讨论】:
【参考方案7】:经过几次迭代,我已经能够针对这个问题改进一个解决方案,该解决方案使用基于堆栈的方法来避免递归,从而稍微简化了一些事情。
/***
* @name array_flatten
* @author Tom Penzer @tpenzer
* Flattens a multi-tiered array into a single-tiered
* associative array with keys reflective of their
* values' hierarchy.
*
* @param array $array Required - the multi-
* level keyed array to be flattened
* @param string $separator Optional - the string
* used to separate the keys from different levels of
* the hierarchy
*
* @return array a single-level keyed array
***/
function array_flatten($array, $separator = '_')
$output = array();
while (list($key, $value) = each($array))
if (is_array($value))
$build = array();
foreach ($value as $s_key => $s_value)
$build[$key . $separator . $s_key] = $s_value;
unset($array[$key]);
$array = $build + $array;
unset($build);
continue;//skip write to $output
$output[$key] = $value;
unset($array[$key]);
return $output;
不完全是请求的方法,但它与解决问题的递归方法形成了很好的对比。
【讨论】:
【参考方案8】:这将展平一个多维关联数组,如果它是重复的,则将一个数字附加到键上。 如果您不介意使用数字索引来区分重复键而不是连接键,这可能是一个解决方案。
$result = array();
array_walk_recursive($your_array, function($v, $k) use (&$result) $i = ""; for (; isset($result[$k."$i"]); $i++); $result[$k."$i"] = $v; );
我怀疑它可以进一步处理连接键。
上面的解决方案基本上是为了做这种事情
<?php
$xml_str = "
<images>
<image>
<position>0</position>
</image>
<image1>
<position>10</position>
</image1>
</images>";
// turn the xml into a multidimentional array
$ob = simplexml_load_string($xml_str);
$json = json_encode($ob);
$my_array = json_decode($json, true);
print_r($my_array);
// flatten it
$result = array();
array_walk_recursive($my_array, function($v, $k) use (&$result) $i = ""; for (; isset($result[$k."$i"]); $i++); $result[$k."$i"] = $v; );
print_r($result);
?>
【讨论】:
array_walk_recursive()
忽略所有非叶节点。 3v4l.org/Ca4m2 这种技术不可能为这个问题提供所需的输出。换句话说,这是对另一个问题的正确答案。以上是关于PHP在连接键时将嵌套数组转换为单个数组?的主要内容,如果未能解决你的问题,请参考以下文章
当字符串包含 PHP 中的重复键时,如何将字符串转换为关联数组? [复制]