如何将多维数组扁平化?

374

在PHP中,是否有可能在不使用递归或引用的情况下将(双/多)维数组展开?

我只对值感兴趣,可以忽略键,我考虑使用array_map()array_values()来实现。


18
为什么要避免使用递归?递归指的是一个函数在其内部调用自身。虽然递归能够解决某些问题,但它也有缺点。首先,递归需要更多的内存空间,因为每次函数调用时都会创建一个新的栈帧。如果递归层数过多,就会导致栈溢出。此外,递归通常比迭代执行速度慢,因为每次调用都需要一定的开销。因此,在编写代码时,应该尽可能避免使用递归,除非递归是必须的或者使用递归可以使代码更加清晰易懂。 - JorenB
6
主要是重复的问题 https://dev59.com/oXRB5IYBdhLWcg3wv5o_ - cletus
4
如果你想处理任意深度的数组中的所有元素,没有递归是不可能的(虽然你可以把它伪装成迭代,但本质上并没有区别)。如果你只是想避免自己编写递归处理代码,可以使用http://dk2.php.net/manual/en/function.array-walk-recursive.php函数,并提供一个回调函数将元素添加到可用的数组中(使用全局变量、userdata参数、将其全部放在一个类中并引用$this等方法均可) 。 - Michael Madsen
@JorenB:我希望能看到一个实现可以被实现的情况。 - Alix Axel
请查看Nspl中的flatten函数。您还可以使用它来指定深度。 - Ihor Burlachenko
31个回答

1

如果您正在寻找一个真正干净的解决方案,那么这里有一个选项:

使用一个包含各种键值配置的数组数组:

$test_array = array(
    array('test' => 0, 0, 0, 0),
    array(0, 0, 'merp' => array('herp' => 'derp'), 0),
    array(0, 0, 0, 0),
    array(0, 0, 0, 0)
);
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($test_array));
var_dump( iterator_to_array($it, false) ) ; 

这将仅获取每个数组中的值并返回一个单一的扁平化数组。
结果中的值输出:
0 0 0 0 0 0 derp 0 0 0 0 0 0 0 0 0

我没有点踩,但我可以想象一个可能被认为不是答案有帮助的原因是,这个回答与2009年的被接受的答案帖子相似,至少建议了相同的内容。请比较您自己的回答并发表评论。 - hakre

0

因为这里的代码看起来很可怕。这是一个函数,它还可以将多维数组转换为与HTML表单兼容的语法,但更易于阅读。

/**
 * Flattens a multi demensional array into a one dimensional
 * to be compatible with hidden html fields.
 *
 * @param array $array
 *  Array in the form:
 *  array(
 *    'a' => array(
 *      'b' => '1'
 *    )
 *  )
 *
 * @return array
 *  Array in the form:
 *  array(
 *    'a[b]' => 1,
 *  )
 */
function flatten_array($array) {
  // Continue until $array is a one-dimensional array.
  $continue = TRUE;
  while ($continue) {
    $continue = FALSE;

    // Walk through top and second level of $array and move 
    // all values in the second level up one level.
    foreach ($array as $key => $value) {
      if (is_array($value)) {
        // Second level found, therefore continue.
        $continue = TRUE;

        // Move each value a level up.
        foreach ($value as $child_key => $child_value) {
          $array[$key . '[' . $child_key . ']'] = $child_value;
        }

        // Remove second level array from top level.
        unset($array[$key]);
      }
    }
  }

  return $array;
}

0

根据要求,采用非递归、非引用的实现方式,可能比基于递归的实现更易于理解。可以处理任意深度的多维数组,但无法展开关联数组。它通过每个循环将数组展平一层,直到完全有效。

function array_flatten(): array{
    $result = func_get_args();
    // check all elements of $list are not arrays
    $_is_flat = function (array $list): bool {
        foreach ($list as $val) {
            if (is_array($val)) {
                return false;
            }
        }
        return true;
    };
    do {
        $tmp = [];
        foreach ($result as $val) {
            if (is_array($val)) {
                if (!array_is_list($val)) {
                    throw new \Exception(sprintf("array_flatten can't handle associative arrays: %s", json_encode($val)));
                }
                $tmp = array_merge($tmp, $val);
            } else {
                $tmp[] = $val;
            }
        }
        $result = $tmp;
    } while (!$_is_flat($result));
    return $result;
}

它处理的案例如下:

assertEquals(array_flatten(1, 2), $expected = [1, 2], 'array_flatten 1a');
assertEquals(array_flatten([1], [2]), $expected = [1, 2], 'array_flatten 1b');
assertEquals(array_flatten([1], [[2], 3]), $expected = [1, 2, 3], 'array_flatten 1c');
assertEquals(array_flatten(1, [2, 3], [4, 5]), $expected = [1, 2, 3, 4, 5], 'array_flatten 2');
assertEquals(array_flatten(2, 3, [4, 5], [6, 7], 8), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 3');
assertEquals(array_flatten([2, 3, [4, 5], [6, 7], 8]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 4');
assertEquals(array_flatten([2, [3, [4, [5]], [6, [7]], 8]]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten complex');

这个“更容易理解”吗?为什么要禁止关联键? - mickmackusa

0
如果您想保留中间键:
function flattenArray(array &$result, $value, string $key = "")
{
    if (!is_array($value)) {
        $result[$key] = $value;
        return $result;
    }
    foreach ($value as $subKey => $subArray) {
        $newKey = $key !== "" ? $key . "_" . $subKey : $subKey;
        flattenArray($result, $subArray, $newKey);
    }
    return $result;
}

$nestedArray = [
    "name" => "John",
    "pets" => [
        ["id" => 1, "name" => "snooop"],
        ["id" => 2, "name" => "medor"],
    ],
    "job" => ["title" => "developper"],
];

$intermediateResult = [];
$flattened = flattenArray($intermediateResult, $nestedArray);
var_dump($flattened);

这将输出:

array(6) {
["name"]=>
  string(4) "John"
        ["pets_0_id"]=>
  int(1)
  ["pets_0_name"]=>
  string(6) "snooop"
        ["pets_1_id"]=>
  int(2)
  ["pets_1_name"]=>
  string(5) "medor"
        ["job_title"]=>
  string(10) "developper"
}

请查看https://ideone.com/KXLtzZ#stdout


0

针对php 5.2版本

function flatten(array $array) {
    $result = array();

    if (is_array($array)) {
        foreach ($array as $k => $v) {
            if (is_array($v)) {
                $result = array_merge($result, flatten($v));
            } else {
                $result[] = $v;
            }
        }
    }

    return $result;
}

请在这个仅包含代码的答案中加入一些解释。 - mickmackusa
如果函数签名已经要求传入的数据是数组类型,为什么还需要 if (is_array($array)) { 呢? - mickmackusa

0

这个版本可以进行深度、浅层或特定层数的操作:

/**
 * @param  array|object $array  array of mixed values to flatten
 * @param  int|boolean  $level  0:deep, 1:shallow, 2:2 levels, 3...
 * @return array
 */
function flatten($array, $level = 0) {
    $level = (int) $level;
    $result = array();
    foreach ($array as $i => $v) {
        if (0 <= $level && is_array($v)) {
            $v = flatten($v, $level > 1 ? $level - 1 : 0 - $level);
            $result = array_merge($result, $v);
        } elseif (is_int($i)) {
            $result[] = $v;
        } else {
            $result[$i] = $v; 
        }
    }
    return $result;
}

除了解释这段代码可以做什么之外,请向未来的研究人员解释它是如何工作的。 - mickmackusa

-1
<?php
//recursive solution

//test array
$nested_array = [[1,2,[3]],4,[5],[[[6,[7=>[7,8,9,10]]]]]];

/*-----------------------------------------
function call and return result to an array
------------------------------------------*/
$index_count = 1;
$flatered_array = array();
$flatered_array = flat_array($nested_array, $index_count);

/*-----------------------------------------
Print Result
-----------------------------------------*/
echo "<pre>";
print_r($flatered_array);


/*-----------------------------------------
function to flaten an array 
-----------------------------------------*/
function flat_array($nested_array, & $index_count, & $flatered_array) {

  foreach($nested_array AS $key=>$val) {
      if(is_array($val)) {
        flat_array($val, $index_count, $flatered_array);
      }
      else {
        $flatered_array[$index_count] = $val;
        ++$index_count;
      }      
  }

return $flatered_array;
}
?>

-1
这是一个简单的方法:
$My_Array = array(1,2,array(3,4, array(5,6,7), 8), 9);

function checkArray($value) {
    foreach ($value as $var) {
        if ( is_array($var) ) {
            checkArray($var);
        } else {
            echo $var;
        }
    }
}

checkArray($My_Array);

-1

我需要将PHP多维数组以HTML输入格式表示。

$test = [
    'a' => [
        'b' => [
            'c' => ['a', 'b']
        ]
    ],
    'b' => 'c',
    'c' => [
        'd' => 'e'
    ]
];

$flatten = function ($input, $parent = []) use (&$flatten) {
    $return = [];

    foreach ($input as $k => $v) {
        if (is_array($v)) {
            $return = array_merge($return, $flatten($v, array_merge($parent, [$k])));
        } else {
            if ($parent) {
                $key = implode('][', $parent) . '][' . $k . ']';

                if (substr_count($key, ']') != substr_count($key, '[')) {
                    $key = preg_replace('/\]/', '', $key, 1);
                }
            } else {
                $key = $k;
            }           

            $return[$key] = $v;
        }
    }

    return $return;
};

die(var_dump( $flatten($test) ));

array(4) {
  ["a[b][c][0]"]=>
  string(1) "a"
  ["a[b][c][1]"]=>
  string(1) "b"
  ["b"]=>
  string(1) "c"
  ["c[d]"]=>
  string(1) "e"
}

https://github.com/alixaxel/phunction/blob/7e8f47049d522609a414498f4dfd023dfa82a0d0/phunction.php#L269-L304 - Alix Axel
@AlixAxel 这条评论和这篇文章有什么关系吗?是错发了吗? - Gajus
我认为这与你正在做的事情非常相似,所以决定分享一下,我认为唯一的区别是我的表示方法也适用于PHP - 形式为$var['a']['b']['c'][0] = 'a'; ... - Alix Axel
我故意需要HTML输出。不过还是谢谢你的分享。 - Gajus
@gajus-kuizinas这就是我也发布了这个回答的原因:https://dev59.com/qnM_5IYBdhLWcg3wlEFH#30463922。没有冒犯的意思。 - Gellweiler
2
我觉得这是对错误问题的正确答案。在回答时,请尽量按照提问的问题来回答,否则页面可能会偏离核心问题,让未来的研究者感到困惑。 - mickmackusa

-1
如果您有一个对象数组并想要使用节点扁平化它,只需使用此函数:
function objectArray_flatten($array,$childField) {
    $result = array();
    foreach ($array as $node)
    {
        $result[] = $node;
        if(isset($node->$childField))
        {
            $result = array_merge(
                $result, 
                objectArray_flatten($node->$childField,$childField)
            );
            unset($node->$childField);
        }

    }
    return $result;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接