使用array_map()函数并向映射函数传递两个参数 - array_map(): 参数#3应该是一个数组。

32

我有一个抽象类,看起来像这样:

abstract class Transformer {

    /**
     * Transform a collection of items
     *
     * @param array $items
     * @param bool $format
     * @return array
     */
    public function transformCollection(array $items, $format)
    {
        return array_map([$this, 'transform'], $items, $format);
    }

    /**
     * Transform a item
     *
     * @param array $item
     * @param bool $format
     * @return mixed
     */
    public abstract function transform(array $item, $format);

}

然后我有以下实现它的类:

class ServiceLogTransformer extends Transformer {

    public function transform(array $service_log, $format = false)
    {
        return [
            'id'    => $service_log['id'],
            'date'  => $service_log['log_date'],
            'time'  => $service_log['log_time'],
            'type'  => ($format ? status_label($service_log['log_type']) : $service_log['log_type']),
            'entry' => $service_log['log_entry']
        ];
    }

}
当运行此代码时,我会收到以下错误: array_map():参数#3应该是一个数组 在类中调用array_map函数时如何传递2个或更多参数? 我查看了PHP文档,并且似乎允许这样做,但在我的Larave 4.2项目上无法工作。 有任何想法吗?
4个回答

73

请务必阅读文档:

http://php.net/manual/zh/function.array-map.php

array array_map ( callable $callback , array $array1 [, array $... ] )

然而你传递布尔型参数$format

"在类中调用array_map函数时如何传递2个或更多参数?

我会使用use()语法创建匿名函数。

public function transformCollection(array $items, $format)
{
    return array_map(function($item) use ($format) {
        return $this->transform($item, $format);
    }, $items);
}

15

您不能使用array_map将硬编码的值传递给回调函数(在功能语言中通常称为柯里化或部分应用函数)。array_map所需的是不定数量的数组,所有这些数组的元素数量都应相同。每个数组当前索引处的元素将作为单独的参数传递给回调函数。因此,例如,如果您执行以下操作:

$arr1 = [1, 2, 3, 4];
$arr2 = [2, 4, 6, 8];

$func = function ($a, $b) { return $a.'-'.$b; };


$arr3 = array_map($func, $arr1, $arr2);

您得到这个东西:
['1-2', '2-4', '3-6', '4-8']

希望这能解释它背后的思想 - 每个传入的数组都将使用第一个传递的数组中当前位置的元素作为相关参数传递给回调函数。
所以,正如我所说的,它不用于向回调传递“静态”值。然而,您可以通过即时定义匿名函数来自行实现此操作。在您的 transformCollection 方法中:
return array_map(function ($item) use ($format) {
    return $this->transform($item, $format);
}, $items);

8

大多数答案都显示,使用use关键字的匿名函数是将额外参数传递给其他可调用函数的典型方式。

abstract class Transformer {
    public function transformCollection(array $items, $format)
    {
        return array_map(function($item) use ($format) {
            return $this->transform($item, $format);
        }, $items);
    }
}

很可能,这种情况最好使用标准的foreach循环遍历array_map,因为它可能更有效率和易于阅读。这也可以防止索引被重新编号,并且与TraversableArrayAccess项目一起工作。

abstract class Transformer {
    public function transformCollection(array $items, $format)
    {
        foreach($items as $key => $item) {
           $items[$key] = $this->transform($item, $format);
        }
        return $items;
    }
}

如果你坚持要使用 array_map,但匿名函数在你的环境中无法使用(即 PHP 5.3 之前的版本),并且你需要将 $format 作为第二个参数传递,则需要将 $format 转换为与 $items 相同长度的数组。
abstract class Transformer {
    public function transformCollection(array $items, $format)
    {
        // Fill an array of same length as $items with the $format argument.
        $format = array_fill(0, count($items), $format);
        return array_map([$this, 'transform'], $items, $format);
    }
}

编辑:

我最近意识到,由于您正在使用实例来转换数据,因此还有另一种可用的选项。它涉及将$format存储到实例中,可能使用setter,并在提供为参数时进行覆盖。这样,它可以通过使用$this->format在转换方法中访问。

abstract class Transformer {
    protected $format;

    public function setFormat($format)
    {
        $this->format = $format;
    }

    public function transformCollection(array $items, $format = null)
    {
        if (isset($format)) {
            $this->setFormat($format);
        }
        // ...
    }

    // ...
}

2
这可能不适用于laravel 4.2 // pre php 5.3(如Shaun所述),但对于遇到此问题的一些人可能会有所帮助。
abstract class Transformer {

    /**
     * Transform a collection of items
     *
     * @param array $items
     * @param bool $format
     * @return array
     */
    public function transformCollection(array $items, $format)
    {
        $args = func_get_args();
        return $this->mapWith([$this, 'transform'], $args);
    }

    /**
     * @param callback<array|string> $callback
     * @param array $args first a collection to disect, then optional additional arguments to the callback
     * @return array
     */
    private function mapWith($callback, $args) {
        $data = array_shift($args);
        $closure = \Closure::fromCallable($callback);
        $scope = \is_array($callback) ? $callback[0] : null;
        return array_map(function ($item) use ($scope, $closure, $args) {
            array_unshift($args, $item);
            if (null !== $scope) {
                array_unshift($args, $scope);
                $closure = [$closure, 'call'];
            }
            return \call_user_func_array($closure, $args);
        }, $data);
    }

    /**
     * Transform a item
     *
     * @param array $item
     * @param bool $format
     * @return mixed
     */
    public abstract function transform(array $item, $format);

}

function status_label($index){return vsprintf('label: %s', [$index,]);}

#Then I have the following class that implements it:

class ServiceLogTransformer extends Transformer {

    public function transform(array $service_log, $format = false)
    {
        return [
            'id'    => $service_log['id'],
            'date'  => $service_log['log_date'],
            'time'  => $service_log['log_time'],
            'type'  => ($format ? status_label($service_log['log_type']) : $service_log['log_type']),
            'entry' => $service_log['log_entry']
        ];
    }

}


$logs = [
['id' => 123454, 'log_date'=>'20180926', 'log_time'=>'151913', 'log_type'=>'q', 'log_entry' => 'lorem',],
['id' => 353454, 'log_date'=>'20180926', 'log_time'=>'152013', 'log_type'=>'r', 'log_entry' => 'dolor',],
];

$slt = new ServiceLogTransformer();
$new = $slt->transformCollection($logs, false);
$lab = $slt->transformCollection($logs, true);
var_dump($logs);
var_dump($new);
var_dump($lab);

因此,这是一种动态使用方式,通过在php的匿名函数下使用Closure类的call方法。如果回调是一个数组,它将把->call的范围绑定到第一个数组元素,这应该是方法对象。


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