281

有没有办法做这样的事情:

$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_keysand array_values,而是直接传递$test_array变量?

所需的输出是:

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

19 回答 19

262

不适用于 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"
// }
于 2012-10-23T17:51:18.810 回答
203

这可能是最短和最容易推理的:

$states = array('az' => 'Arizona', 'al' => 'Alabama');

array_map(function ($short, $long) {
    return array(
        'short' => $short,
        'long'  => $long
    );
}, array_keys($states), $states);

// produces:
array(
     array('short' => 'az', 'long' => 'Arizona'), 
     array('short' => 'al', 'long' => 'Alabama')
)
于 2015-05-29T21:28:00.463 回答
86

这是我非常简单的、与 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"
}

部分应用

如果你需要多次使用不同数组但映射函数相同的函数,你可以做一些叫做偏函数应用的事情(与 '<a href="https://en.wikipedia.org/wiki/Currying" rel="noreferrer">currying'),它允许您仅在调用时传入数据数组:

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. 如前所述,返回重复键将导致后面的值获胜。

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

注意=>代替,in $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成一个高阶函数,这可能已经足够好了。

于 2017-03-24T16:49:16.010 回答
47
$array = [
  'category1' => 'first category',
  'category2' => 'second category',
];
 
$new = array_map(function($key, $value) {
  return "{$key} => {$value}";
}, array_keys($array), $array);
于 2020-05-05T11:49:21.287 回答
21

使用 PHP5.3 或更高版本:

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

var_dump(
    array_map(
        function($key) use ($test_array) { return "$key loves ${test_array[$key]}"; },
        array_keys($test_array)
    )
);
于 2014-01-28T13:45:06.030 回答
6

看这里!有一个简单的解决方案!

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));
于 2018-09-05T10:27:43.947 回答
6

我将使用 5.6 或更高版本为该问题添加另一个解决方案。不知道它是否比已经很棒的解决方案更有效(可能不是),但对我来说它更容易阅读:

$myArray = [
    "key0" => 0,
    "key1" => 1,
    "key2" => 2
];

array_combine(
    array_keys($myArray),
    array_map(
        function ($intVal) {
            return strval($intVal);
        },
        $myArray
    )
);

使用strval()中的示例函数array_map,这将生成:

array(3) {
  ["key0"]=>
  string(1) "0"
  ["key1"]=>
  string(1) "1"
  ["key2"]=>
  string(1) "2"
}

希望我不是唯一一个觉得这很容易掌握的人。 从一个键数组和一个值数组array_combine创建一个数组,其余的很容易解释。key => value

于 2018-10-02T10:44:56.633 回答
3

通过“手动循环”,我的意思是编写一个使用foreach. 这会像 do 一样返回一个新数组array_map,因为函数的作用域$array是一个副本——而不是一个引用:

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

您使用array_mapwitharray_keys虽然的技术实际上看起来更简单并且更强大,因为您可以将null其用作回调来返回键值对:

function map($array, callable $fn = null) {
  return array_map($fn, array_keys($array), $array);
}
于 2012-10-23T17:45:57.720 回答
3

这就是我在我的项目中实现这一点的方式。

function array_map_associative(callable $callback, $array) {
    /* map original array keys, and call $callable with $key and value of $key from original array. */
    return array_map(function($key) use ($callback, $array){
        return $callback($key, $array[$key]);
    }, array_keys($array));
}
于 2018-03-06T21:57:07.867 回答
3

如果您只需要一次,则关闭将起作用。我会使用发电机

$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)
));
于 2020-09-08T22:57:14.773 回答
1

根据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);
于 2012-10-23T21:10:24.887 回答
1

我根据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))
于 2015-04-01T20:25:13.977 回答
1

YaLinqo库* 非常适合此类任务。它是 .NET 的 LINQ 端口,它完全支持所有回调中的值和键,并且类似于 SQL。例如:

$mapped_array = from($test_array)
    ->select(function ($v, $k) { return "$k loves $v"; })
    ->toArray();

要不就:

$mapped_iterator = from($test_array)->select('"$k loves $v"');

'"$k loves $v"'是该库支持的完整闭包语法的快捷方式。toArray()到底是可选的。方法链返回一个迭代器,因此如果只需要使用 using 迭代结果foreachtoArray则可以删除 call 。

* 由我开发

于 2015-06-04T13:36:39.940 回答
1

我总是喜欢数组映射的 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;
    });
);
于 2018-01-09T14:40:22.120 回答
1

我会做这样的事情:

<?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"
}
于 2018-08-15T03:50:07.700 回答
1

您可以使用此数组库中的map方法来轻松实现您想要的内容:

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

它还保留键并返回新数组,更不用说满足您需求的几种不同模式了。

于 2019-07-26T19:47:45.563 回答
0

另一种使用(出)保留键的方法:

$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)
);
于 2018-11-21T00:08:17.260 回答
0

您可以在函数上传递 2 个参数来获取数组的键和值。像:

array_map(function($key, $value) {
    return [$key => $value];
}, array_keys($my_array), $my_array);
于 2022-03-05T21:56:30.167 回答
-2

我看到它缺少明显的答案:

function array_map_assoc(){
    if(func_num_args() < 2) throw new \BadFuncionCallException('Missing parameters');

    $args = func_get_args();
    $callback = $args[0];

    if(!is_callable($callback)) throw new \InvalidArgumentException('First parameter musst be callable');

    $arrays = array_slice($args, 1);

    array_walk($arrays, function(&$a){
        $a = (array)$a;
        reset($a);
    });

    $results = array();
    $max_length = max(array_map('count', $arrays));

    $arrays = array_map(function($pole) use ($max_length){
        return array_pad($pole, $max_length, null);
    }, $arrays);

    for($i=0; $i < $max_length; $i++){
        $elements = array();
        foreach($arrays as &$v){
            $elements[] = each($v);
        }
        unset($v);

        $out = call_user_func_array($callback, $elements);

        if($out === null) continue;

        $val = isset($out[1]) ? $out[1] : null;

        if(isset($out[0])){
            $results[$out[0]] = $val;
        }else{
            $results[] = $val;
        }
    }

    return $results;
}

与array_map 完全一样。几乎。

实际上,它并不map像您从其他语言中所知道的那样纯粹。Php 很奇怪,所以它需要一些很奇怪的用户函数,因为我们不想破坏我们精确破坏worse is better的方法。

真的,这根本不是map。然而,它仍然非常有用。

  • 与 array_map 的第一个明显区别是回调each()从每个输入数组中获取输出而不是单独的值。您仍然可以一次遍历更多数组。

  • 第二个区别是键从回调返回后的处理方式;回调函数的返回值应该是array('new_key', 'new_value'). 键可以并且将被更改,如果返回相同的键,相同的键甚至会导致以前的值被覆盖。这不是常见的map行为,但它允许您重写密钥。

  • 第三个奇怪的事情是,如果您key在返回值中省略( byarray(1 => 'value')array(null, 'value')),则将分配新键,就像$array[] = $value使用过一样。这也不map是常见的行为,但我猜它有时会派上用场。

  • 第四个奇怪的事情是,如果回调函数没有返回值,或者返回null,则输出中省略了当前键和值的整个集合,它只是被跳过了。这个功能完全是un mappy,但如果有这样的功能,它将使这个功能成为优秀的特技替身array_filter_assoc

  • 如果您在回调的返回中省略第二个元素 ( 1 => ...)(null部分),则使用而不是实际值。

  • 除了带有键01回调返回的元素之外的任何其他元素都将被忽略。

  • 最后,如果 lambda 返回除 ofnull或数组之外的任何值,则将其视为键和值都被省略,因此:

    1. 分配了元素的新键
    2. null被用作它的价值
警告:
请记住,最后一个功能只是以前功能的残余,它可能完全没用。强烈建议不要依赖此功能,因为此功能将在未来的版本中被随机弃用和意外更改。

注意:
与 in 不同array_map,传递给 的所有非数组参数array_map_assoc(第一个回调参数除外)都被默默地转换为数组。

例子:
// TODO: examples, anyone?

于 2013-07-22T20:22:55.873 回答