有没有这样做的方法:

$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"
}

当前回答

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

其他回答

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

看过来!有一个简单的解决方案!

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

正如问题中所述,array_map已经具备所需的功能。这里的其他答案严重地把事情复杂化了:array_walk不是函数式的。

使用

正如你从例子中所期望的那样:

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

var_dump(array_map2(function($a, $b) { return "$a loves $b"; }, $test_array));

如果你只需要它一次,闭包就可以了。我会用发电机。

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

$x_result = (function(array $arr) {
    foreach ($arr as $key => $value) {
        yield "$key loves $value";
    }
})($test_array);

var_dump(iterator_to_array($x_result));

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

对于可重复使用的东西:

function xmap(callable $cb, array $arr)
{
    foreach ($arr as $key => $value) {
        yield $cb($key, $value);
    }
}

var_dump(iterator_to_array(
    xmap(function($a, $b) { return "$a loves $b"; }, $test_array)
));

我根据eis的答案做了这个函数:

function array_map_($callback, $arr) {
    if (!is_callable($callback))
        return $arr;

    $result = array_walk($arr, function(&$value, $key) use ($callback) {
        $value = call_user_func($callback, $key, $value);
    });

    if (!$result)
        return false;

    return $arr;
}

例子:

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

var_dump(array_map_(function($key, $value){
    return $key . " loves " . $value;
}, $arr));

输出:

array (
  'first_key' => 'first_key loves first_value,
  'second_key' => 'second_key loves second_value',
)

当然,您可以使用array_values来返回OP想要的内容。

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