是否有类似于Python的zip函数的PHP函数?

75

Python有一个很好的zip()函数。 PHP有相对应的函数吗?


6
标题并不能说明一切。如果我很熟悉PHP,但不懂Python怎么办?你至少可以解释一下Python的“zip”函数的用途和用法。 - Dean Harding
3
谢谢,我已经包含了文档的链接。 - Craig
@craig,你提供的“一个好用的zip()函数”的解释链接如下:
创建一个迭代器,从每个可迭代对象中聚合元素。
返回一个元组的迭代器,其中第i个元组包含来自每个参数序列或可迭代对象的第i个元素。当最短的输入可迭代对象耗尽时,迭代器停止。对于单个可迭代对象参数,它返回一个1元组的迭代器。如果没有参数,则返回一个空迭代器。...
我80%确定这就是我要找的问题,但是...你的回答并没有帮到我,反而有些晦涩难懂。
- BrianHenryIE
1
这个问题不适合作为其他Stack Overflow页面的重复目标,因为它缺少一个 [mcve] 并且对实际需求没有进行必要的解释 -- 你需要是一个Python程序员或者点击一个链接才能理解其含义。PHP中数组转置的规范(被许多Stack Overflow页面所链接):Transposing multidimensional arrays in PHP - mickmackusa
14个回答

113
只要所有的数组长度相同,您可以使用null作为第一个参数和array_map一起使用。
array_map(null, $a, $b, $c, ...);

如果一些数组较短,它们将被填充为最长数组的长度,与Python不同,返回结果是最短数组的长度。


2
你真的尝试过吗?array_map 的行为不像 Python 的 ziparray_map(null, array(1,2), array(3)) 返回 array(array(1,3), array(2,null)),而 zip([1,2], [3]) 只返回 [(1,2)] - Gumbo
7
虽然这是符合 Ruby 的方式,而不是 Python 的方式,但我认为它足够接近大多数用途。通常情况下,您不会有意地将不同长度的数组传递给 zip。我已经在答案中添加了对此的解释。 - rjmunro
1
+1 实际上更像是itertools.izip_longest(..., fillvalue=None),但它非常接近并且仍然是最好的解决方案。如果您想要与Python的zip()完全相同的结果,可以使用array_slice(array_map(null, $a, $b), 0, min(count($a), count($b))),以可能是最有效的方式获得您想要的结果。 - Tadeck
7
关于这种方法的另一件事是,如果你使用它在单个数组上而不是多个数组上,它会表现得非常奇怪。如果你运行array_map(null,array(1,2,3)),你并不会像你期望的那样得到array(array(1),array(2),array(3)),而只是得到原始值的一个数组,即array(1,2,3)。这可能不会影响大多数人,但如果你试图将任意数量的数组合并在一起(例如,如果数组的数量由对数据库的查询所定义),那么这将变成一个非常令人懊恼的问题。如果使用得当,则是一个不错的解决方案。 - Josiah

21

array_combine 函数可以实现类似的功能。

否则就自行编写代码实现:

function array_zip($a1, $a2) {
  for($i = 0; $i < min(length($a1), length($a2)); $i++) {
    $out[$i] = [$a1[$i], $a2[$i]];
  }
  return $out;
}

2
应该用“count”而不是“length”,对吧? - lacek

13

尝试使用此函数创建类似于 Python 的 zip 的数组:

function zip() {
    $args = func_get_args();
    $zipped = array();
    $n = count($args);
    for ($i=0; $i<$n; ++$i) {
        reset($args[$i]);
    }
    while ($n) {
        $tmp = array();
        for ($i=0; $i<$n; ++$i) {
            if (key($args[$i]) === null) {
                break 2;
            }
            $tmp[] = current($args[$i]);
            next($args[$i]);
        }
        $zipped[] = $tmp;
    }
    return $zipped;
}

您可以使用任意数量的数组以及任意数量的项将此函数传递。


10

这个与Python的zip()函数完全相同,并且也兼容PHP<5.3:

function zip() {
    $params = func_get_args();
    if (count($params) === 1){ // this case could be probably cleaner
        // single iterable passed
        $result = array();
        foreach ($params[0] as $item){
            $result[] = array($item);
        };
        return $result;
    };
    $result = call_user_func_array('array_map',array_merge(array(null),$params));
    $length = min(array_map('count', $params));
    return array_slice($result, 0, $length);
};

这个函数会按照 Python 的 zip() 函数的方式合并数组,并且不会返回最短数组结束后发现的元素。

以下是示例:

zip(array(1,2,3,4,5),array('a','b'));

给出以下结果:

array(array(1,'a'), array(2,'b'))

以及以下内容:

zip(array(1,2,3,4,5),array('a','b'),array('x','y','z'));

得到以下结果:

array(array(1,'a','x'), array(2,'b','y'))

查看此演示,以证明上述内容。

编辑:添加接收单个参数的支持(在这种情况下,array_map 的行为有所不同;感谢 Josiah)。


1
不完全像Python的。将此PHP示例Python中实现的相同示例进行比较。在您的版本中,对单个数组进行压缩会返回原始数组,在Python中则返回1元组数组。 - Josiah
@Josiah:这是语言差异的结果。即array_map()函数相当不一致的行为:当传递null作为第一个参数时,它会“压缩”其他参数,但如果只有一个附加参数(除了上述的null),它不会被封装在array中(就像有两个或更多的参数那样),而是直接传递。通过检查$params的长度再添加一个条件可以解决这个问题。 - Tadeck
1
没问题,但是现在这么说你的函数正好和Python的zip()函数一样,这是不准确的。我只是想指出来,以防其他人使用这种方法遇到相同的问题,希望能够节省他们一些调试时间。特别是因为这里有其他正确处理该情况的答案。 - Josiah
@Josiah:问题已解决。虽然代码变得丑陋了,但现在支持你的角落情况的代码大约占整个函数的60%,希望对某些人有所帮助;) - Tadeck
也许可以使用 if (count($params) === 1) return array_map(function ($elem){return array($elem);},$params[0]);?我猜这样看起来会好一些,而且代码更简洁。当然,这种方法只适用于 PHP > 5.3,所以这是一个问题。不过没关系,因为我正在使用 PHP 5.3,所以我会采用这种方法。支持改变。 - Josiah

5

解决方案

zip() 非常相似的解决方案,同时使用内置的PHP函数,如下:

array_slice(
    array_map(null, $a, $b, $c), // zips values
    0, // begins selection before first element
    min(array_map('count', array($a, $b, $c))) // ends after shortest ends
);

为什么不直接使用array_map(null, $a, $b, $c)?

正如我在评论中提到的,我倾向于支持nabnabit的解决方案(array_map(null, $a, $b, ...)),但稍作修改(如上所示)。

一般来说,这样做:

array_map(null, $a, $b, $c);

是Python中的对应物:

itertools.izip_longest(a, b, c, fillvalue=None)

如果你想要列表而不是迭代器,可以使用list()将其包装起来。因此,它并不完全符合模仿zip()的行为的要求(除非所有数组的长度都相同)。


2

1
我为我的PHP枚举实现编写了一个zip()函数。
代码已经修改,以允许Python风格的zip()和Ruby风格。区别在注释中解释:
/*
 * This is a Python/Ruby style zip()
 *
 * zip(array $a1, array $a2, ... array $an, [bool $python=true])
 *
 * The last argument is an optional bool that determines the how the function
 * handles when the array arguments are different in length
 *
 * By default, it does it the Python way, that is, the returned array will
 * be truncated to the length of the shortest argument
 *
 * If set to FALSE, it does it the Ruby way, and NULL values are used to
 * fill the undefined entries
 *
 */
function zip() {
    $args = func_get_args();

    $ruby = array_pop($args);
    if (is_array($ruby))
        $args[] = $ruby;

    $counts = array_map('count', $args);
    $count = ($ruby) ? min($counts) : max($counts);
    $zipped = array();

    for ($i = 0; $i < $count; $i++) {
        for ($j = 0; $j < count($args); $j++) {
            $val = (isset($args[$j][$i])) ? $args[$j][$i] : null;
            $zipped[$i][$j] = $val;
        }
    }
    return $zipped;
}

例子:

$pythonzip = zip(array(1,2,3), array(4,5),  array(6,7,8));
$rubyzip   = zip(array(1,2,3), array(4,5),  array(6,7,8), false);

echo '<pre>';
print_r($pythonzip);
print_r($rubyzip);
echo '<pre>';

0
/**
 * Takes an arbitrary number of arrays and "zips" them together into a single 
 * array, taking one value from each array and putting them into a sub-array,
 * before moving onto the next.
 * 
 * If arrays are uneven lengths, will stop at the length of the shortest array.
 */
function array_zip(...$arrays) {
    $result = [];
    $args = array_map('array_values',$arrays);
    $min = min(array_map('count',$args));
    for($i=0; $i<$min; ++$i) {
        $result[$i] = [];
        foreach($args as $j=>$arr) {
            $result[$i][$j] = $arr[$i];
        }
    }
    return $result;
}

使用方法:

print_r(array_zip(['a','b','c'],[1,2,3],['x','y']));

输出:

Array
(
    [0] => Array
        (
            [0] => a
            [1] => 1
            [2] => x
        )

    [1] => Array
        (
            [0] => b
            [1] => 2
            [2] => y
        )

)

0
function zip() {
    $zip = [];
    $arrays = func_get_args();
    if ($arrays) {
        $count = min(array_map('count', $arrays));
        for ($i = 0; $i < $count; $i++) {
            foreach ($arrays as $array) {
                $zip[$i][] = $array[$i];
            }
        }
    }
    return $zip;
}

这个答案缺少教育性的解释。 - mickmackusa

0
// create
$a = array("a", "c", "e", "g", "h", "i");
$b = array("b", "d", "f");
$zip_array = array();

// get length of the longest array
$count = count(max($a, $b));

// zip arrays
for($n=0;$n<$count;$n++){
    if (array_key_exists($n,$a)){
        $zip_array[] = $a[$n];
        }   
    if (array_key_exists($n,$b)){
        $zip_array[] = $b[$n];
        }   
    }

// test result
echo '<pre>'; print_r($zip_array); echo '<pre>';

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