有没有这样做的方法:

$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);

其他回答

我会这样做:

<?php

/**
 * array_map_kv()
 *   An array mapping function to map with both keys and values.
 *
 * @param $callback callable
 *   A callback function($key, $value) for mapping values.
 * @param $array array
 *   An array for mapping.
 */
function array_map_kv(callable $callback, array $array) {
  return array_map(
    function ($key) use ($callback, $array) {
      return $callback($key, $array[$key]); // $callback($key, $value)
    },
    array_keys($array)
  );
}

// use it
var_dump(array_map_kv(function ($key, $value) {
  return "{$key} loves {$value}";
}, array(
  "first_key" => "first_value",
  "second_key" => "second_value",
)));

?>

结果:

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

我所说的“手动循环”是指编写一个使用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);
}

下面是我的非常简单的、兼容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被调用的地方抽象为一个高阶函数,这就足够了。

$array = [
  'category1' => 'first category',
  'category2' => 'second category',
];
 
$new = array_map(function($key, $value) {
  return "{$key} => {$value}";
}, array_keys($array), $array);

我一直喜欢数组映射的javascript变体。最简单的版本是:

/**
 * @param  array    $array
 * @param  callable $callback
 * @return array
 */
function arrayMap(array $array, callable $callback)
{
    $newArray = [];

    foreach( $array as $key => $value )
    {
        $newArray[] = call_user_func($callback, $value, $key, $array);
    }

    return $newArray;
}

现在你可以给它一个回调函数如何构造值。

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

var_dump(
    arrayMap($testArray, function($value, $key) {
        return $key . ' loves ' . $value;
    });
);