如何在不循环的情况下展平一个简单的数组?

Posted

技术标签:

【中文标题】如何在不循环的情况下展平一个简单的数组?【英文标题】:How can I flatten a simple array without looping? 【发布时间】:2013-02-03 08:45:18 【问题描述】:

我想把一个简单的多维数组变成一个更简单的数组。

转动这个:

Array
(
    [0] => Array
        (
            [id] => 123
        )
    [1] => Array
        (
            [id] => 456
        )
    ...
    [999] => Array
        (
            [id] => 789
        )
)

到这样的数组中:

Array
(
    [0] =>  123
    [1] => 456
    ...
    [999] => 789
)

我想这样做不循环使用foreach。这在 php 中可行吗?

这是我已经可以使用foreach 循环解决它的方法:

$newArr = array();
foreach ($arr as $a) 
    $newArr[] = $a['id'];

$arr = $newArr;

我想不循环。你能帮忙吗?

【问题讨论】:

为什么不循环播放? 避免基于“前提”的标准做法是无效的 O(n) 循环是 PHP 开发人员最不必担心的问题 @Ryan 你基本上保证没有人能一眼看懂你的代码。 展望未来,PHP 可能有一个array_column() 函数(虽然可能不在 PHP 5.5.0 中)。 $ids = array_column($array, 'id'); 【参考方案1】:

我很欣赏您希望拥有接近函数式编程和隐式循环的东西,但 PHP 不适合您。它不会以实用的风格自然地表达自己。

reset 函数返回数组中的第一个元素,因此您可以将该函数映射到数组:

array_map('reset', $array)

然而在 PHP 中最快的方法是一个简单的for 循环(不是foreachfor)。这里有一堆不同的扁平化方法。只有包含forforeach 的函数会执行显式循环并包含在比较中。

function flatten_for($arr) 
    $c = count($arr);
    $newarr = array();
    for ($i = 0; $i < $c; $i++) 
        $newarr[] = $arr[$i][0];
    
    return $newarr;



function flatten_for_inplace($arr) 
    $c = count($arr);
    for ($i = 0; $i < $c; $i++) 
        $arr[$i] = $arr[$i][0];
    



function flatten_foreach($arr) 
    $newarr = array();
    foreach ($arr as $value) 
        $newarr[] = $value[0];
    
    return $newarr;


function flatten_foreach_inplace($arr) 
    foreach ($arr as $k => $v) 
        $arr[$k] = $v[0];
    


function flatten_foreach_inplace_ref($arr) 
    foreach ($arr as &$value) 
        $value = $value[0];
    


function flatten_map($arr) 
    return array_map('reset', $arr);


function flatten_walk($arr) 
    array_walk($arr, function(&$v, $k)$v = $v[0];);


function visitor($v, $k, &$a) 
    return $a[] = $v;


function flatten_walk_recursive($arr) 
    $newarr = array();
    array_walk_recursive($arr, 'visitor', $newarr);
    return $newarr;


function reducer($result, $item) 
    return $item[0];


function flatten_reduce($arr) 
    return array_reduce($arr, 'reducer', array());


function flatten_merge($arr) 
    return call_user_func_array('array_merge_recursive', $arr);

这是计时码:

function buildarray($length) 
    return array_map(function($e)return array($e);, range(0, $length));


function timeit($callable, $argfactory, $iterations) 
    $start = microtime(true);
    for ($i = 0; $i < $iterations; $i++) 
        call_user_func($callable, call_user_func($argfactory));
    
    return microtime(true) - $start;


function time_callbacks($callbacks, $argfactory, $iterations) 
    $times = array();
    foreach ($callbacks as $callback) 
        $times[$callback] = timeit($callback, $argfactory, $iterations);
    
    return $times;


function argfactory() 
    return buildarray(1000);


$flatteners = array(
    'flatten_for', 'flatten_for_inplace', 'flatten_foreach',
    'flatten_foreach_inplace', 'flatten_foreach_inplace_ref',
    'flatten_map', 'flatten_walk', 'flatten_walk_recursive',
    'flatten_reduce', 'flatten_merge',
);

$results = time_callbacks($flatteners, 'argfactory', 1000);

var_export($results);

在较旧的 MacBook Pro(Core 2 Duo、2.66 GHz、8GB、PHP 5.3.15 和 Suhosin-Patch)上,我得到以下结果:

array (
  'flatten_for' => 12.793387174606,
  'flatten_for_inplace' => 14.093497991562,
  'flatten_foreach' => 16.71691608429,
  'flatten_foreach_inplace' => 16.964510917664,
  'flatten_foreach_inplace_ref' => 16.618073940277,
  'flatten_map' => 24.578175067902,
  'flatten_walk' => 22.884744882584,
  'flatten_walk_recursive' => 31.647840976715,
  'flatten_reduce' => 17.748590946198,
  'flatten_merge' => 20.691106081009,
)

forforeach 方法之间的差异在较长的数组上更小。

令人惊讶的是(对我来说,无论如何)flatten_merge 仍然比普通的 for 循环慢。我预计 array_merge_recursive 至少会更快,因为它基本上将整个工作交给了 C 函数!

【讨论】:

如果您担心内部数组指针的状态,可以只使用reset 而不是current 令人着迷。这非常有帮助。这个答案需要更多的支持!我期待很快在我自己的盒子上进行测试。 I admire your desire to have something approaching functional programming and implicit looping, but PHP is the wrong language for you. 不幸的是,即使在 2021 年它仍然是真的......【参考方案2】:

你可以map它:

$arr = array_map(function($element) 
    return $element['id'];
, $arr);

由于array_map 可能在内部循环,您可以真正做到不循环:

$arr = array_reduce($arr, function($arr, $element) 
    $arr[] = $element['id'];
    return $arr;
);

但没有理由不循环。没有真正的性能提升,而且代码的可读性可能会降低。

【讨论】:

@Dagon array_reduce 也循环吗? 那么第二句话是假的?【参考方案3】:

目前,最简单的方法是使用 array_column() 函数

$newArray = array_column($array, 'id');

【讨论】:

以上是关于如何在不循环的情况下展平一个简单的数组?的主要内容,如果未能解决你的问题,请参考以下文章

递归地展平数组(不循环)javascript

我可以在不创建新列表/数组的情况下展平数组吗? [复制]

如何在不使用 numpy 的情况下将 2D 列表展平为 1D? [复制]

如何在不使用 for 循环的情况下填充二维数组?

如何在不循环的情况下将数组的内容复制到 C++ 中的 std::vector?

如何在不循环的情况下将数组(Range.Value)传递给本机 .NET 类型?