有没有这样做的方法:

$test_array = array(
    "first_key" => "first_value", 
    "second_key" => "second_value"
);

var_dump(
    array_map(
        function($a, $b) {
            return "$a loves $b";
        }, 
        array_keys($test_array), 
        array_values($test_array)
    )
);

但是不是调用array_keys和array_values,而是直接传递$test_array变量?

期望的输出是:

array(2) {
  [0]=>
  string(27) "first_key loves first_value"
  [1]=>
  string(29) "second_key loves second_value"
}

当前回答

根据eis的回答,为了避免弄乱原始数组,我最终做了以下工作:

$test_array = array("first_key" => "first_value",
                    "second_key" => "second_value");

$result_array = array();
array_walk($test_array, 
           function($a, $b) use (&$result_array) 
           { $result_array[] = "$b loves $a"; }, 
           $result_array);
var_dump($result_array);

其他回答

我所说的“手动循环”是指编写一个使用foreach的自定义函数。它像array_map一样返回一个新数组,因为函数的作用域导致$array是一个复制,而不是一个引用:

function map($array, callable $fn) {
  foreach ($array as $k => &$v) $v = call_user_func($fn, $k, $v);
  return $array;
}

你使用array_map和array_keys的技术实际上看起来更简单,也更强大,因为你可以使用null作为回调来返回键值对:

function map($array, callable $fn = null) {
  return array_map($fn, array_keys($array), $array);
}

不是array_map,因为它不处理键。

array_walk:

$test_array = array("first_key" => "first_value",
                    "second_key" => "second_value");
array_walk($test_array, function(&$a, $b) { $a = "$b loves $a"; });
var_dump($test_array);

// array(2) {
//   ["first_key"]=>
//   string(27) "first_key loves first_value"
//   ["second_key"]=>
//   string(29) "second_key loves second_value"
// }

然而,它确实会改变作为参数给出的数组,所以它不是确切的函数式编程(因为你有这样的问题标记)。此外,正如评论中指出的那样,这只会改变数组的值,因此键不会是您在问题中指定的键。

如果你想,你可以写一个函数来固定上面的点,像这样:

function mymapper($arrayparam, $valuecallback) {
  $resultarr = array();
  foreach ($arrayparam as $key => $value) {
    $resultarr[] = $valuecallback($key, $value);
  }
  return $resultarr;
}

$test_array = array("first_key" => "first_value",
                    "second_key" => "second_value");
$new_array = mymapper($test_array, function($a, $b) { return "$a loves $b"; });
var_dump($new_array);

// array(2) {
//   [0]=>
//   string(27) "first_key loves first_value"
//   [1]=>
//   string(29) "second_key loves second_value"
// }

用(out)保存键的另一种方法:

$test_array = [
    "first_key"     => "first_value",
    "second_key"    => "second_value"
];

$f = function($ar) {
    return array_map(
        function($key, $val) {
            return "{$key} - {$val}";
        },
        array_keys($ar),
        $ar
    );
};

#-- WITHOUT preserving keys
$res = $f($test_array);

#-- WITH preserving keys
$res = array_combine(
    array_keys($test_array),
    $f($test_array)
);

你可以使用map方法从这个数组库中轻松地实现你想要的:

Arr::map($test_array, function($a, $b) { return "$a loves $b"; });

此外,它保留键并返回新的数组,更不用说几个不同的模式来满足您的需求。

下面是我的非常简单的、兼容PHP 5.5的解决方案:

function array_map_assoc(callable $f, array $a) {
    return array_column(array_map($f, array_keys($a), $a), 1, 0);
}

你提供的可调用对象本身应该返回一个包含两个值的数组,即return [key, value]。因此,对array_map的内部调用会生成一个数组的数组。然后通过array_column将其转换回一维数组。

使用

$ordinals = [
    'first' => '1st',
    'second' => '2nd',
    'third' => '3rd',
];

$func = function ($k, $v) {
    return ['new ' . $k, 'new ' . $v];
};

var_dump(array_map_assoc($func, $ordinals));

输出

array(3) {
  ["new first"]=>
  string(7) "new 1st"
  ["new second"]=>
  string(7) "new 2nd"
  ["new third"]=>
  string(7) "new 3rd"
}

部分应用程序

如果你需要多次使用不同数组但相同映射函数的函数,你可以做一些叫做部分函数应用(与' curiling '相关),它允许你在调用时只传入数据数组:

function array_map_assoc_partial(callable $f) {
    return function (array $a) use ($f) {
        return array_column(array_map($f, array_keys($a), $a), 1, 0);
    };
}

...
$my_mapping = array_map_assoc_partial($func);
var_dump($my_mapping($ordinals));

它产生相同的输出,假设$func和$ordinals与前面一样。

注意:如果映射函数为两个不同的输入返回相同的键,则与后一个键关联的值将胜出。反转array_map_assoc的输入数组和输出结果,以允许较早的键获胜。(在我的例子中,返回的键不能冲突,因为它们包含了源数组的键,而源数组的键必须是唯一的。)


替代

下面是上面的一个变体,对某些人来说可能更符合逻辑,但需要PHP 5.6:

function array_map_assoc(callable $f, array $a) {
    return array_merge(...array_map($f, array_keys($a), $a));
}

在这种变体中,你提供的函数(数据数组的映射)应该返回一个只有一行的关联数组,即return [key => value]。 映射可调用对象的结果然后被简单地解包并传递给array_merge。与前面一样,返回重复的键将导致后面的值胜出。

n.b. Alex83690在注释中指出,在这里使用array_replace代替array_merge将保留整型键。Array_replace不修改输入数组,因此对于函数代码是安全的。

如果您使用的是PHP 5.3到5.5,那么下面的代码是等效的。它使用array_reduce和二进制+数组操作符将生成的二维数组转换为一维数组,同时保留键:

function array_map_assoc(callable $f, array $a) {
    return array_reduce(array_map($f, array_keys($a), $a), function (array $acc, array $a) {
        return $acc + $a;
    }, []);
}

使用

这两种变体的用法如下:

$ordinals = [
    'first' => '1st',
    'second' => '2nd',
    'third' => '3rd',
];

$func = function ($k, $v) {
    return ['new ' . $k => 'new ' . $v];
};

var_dump(array_map_assoc($func, $ordinals));

注意在$func中=>而不是。

输出与之前相同,并且每个都可以以与之前相同的方式部分应用。


总结

最初问题的目标是使调用的调用尽可能简单,代价是要调用一个更复杂的函数;特别是,能够将数据数组作为单个参数传递进来,而不分离键和值。使用答案开头提供的函数:

$test_array = ["first_key" => "first_value",
               "second_key" => "second_value"];

$array_map_assoc = function (callable $f, array $a) {
    return array_column(array_map($f, array_keys($a), $a), 1, 0);
};

$f = function ($key, $value) {
    return [$key, $key . ' loves ' . $value];
};

var_dump(array_values($array_map_assoc($f, $test_array)));

或者,仅对于这个问题,我们可以简化为array_map_assoc()函数,该函数删除输出键,因为这个问题不需要它们:

$test_array = ["first_key" => "first_value",
               "second_key" => "second_value"];

$array_map_assoc = function (callable $f, array $a) {
    return array_map($f, array_keys($a), $a);
};

$f = function ($key, $value) {
    return $key . ' loves ' . $value;
};

var_dump($array_map_assoc($f, $test_array));

所以答案是否定的,你不能避免调用array_keys,但是你可以把array_keys被调用的地方抽象为一个高阶函数,这就足够了。