PHP array_map包括键

时间:2012-10-23 17:40:00

标签: php functional-programming

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

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

所需的输出是:

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

18 个答案:

答案 0 :(得分:157)

不使用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"
// }

答案 1 :(得分:114)

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

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

答案 2 :(得分:51)

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

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

您提供的callable本身应返回一个包含两个值的数组,即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"
}

部分申请

如果你需要多次使用不同数组但具有相同映射功能的函数,你可以做一些名为partial function application(与'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。如前所述,返回重复键将导致后来的值获胜。

  

n.b。 Alex83690在评论中指出,在array_replace中使用array_merge代替array_replace将保留整数键。 array_reduce不会修改输入数组,因此对于功能代码是安全的。

如果您使用的是PHP 5.3到5.5,则以下内容是等效的。它使用+和二进制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

所以答案是 NO ,你无法避免调用array_keys,但你可以抽象出DELIMITER // CREATE PROCEDURE SP_LOGIN(IN ID VARCHAR(50),IN PASSWORD VARCHAR(50) ) BEGIN SELECT * FROM USERS WHERE ID=ID AND PASSWORD=PASSWORD; END // DELIMITER ; 被调用到更高阶函数的地方,这可能足够好了。

答案 3 :(得分:20)

使用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)
    )
);

答案 4 :(得分:7)

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

答案 5 :(得分:4)

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

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

答案 6 :(得分:2)

基于eis's answer,这是我最终做的,以避免弄乱原始数组:

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

答案 7 :(得分:2)

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

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

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

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

答案 8 :(得分:2)

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()最后是可选的。方法链返回一个迭代器,因此如果需要使用foreach迭代结果,则可以删除toArray调用。

*由我开发

答案 9 :(得分:1)

我根据eis's answer

制作了这个功能
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))

答案 10 :(得分: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"
}

答案 11 :(得分:1)

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

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

答案 12 :(得分:1)

我将使用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数组,其余内容很容易解释。

答案 13 :(得分:0)

如果您只需要一次closure,则它会起作用。我会使用generator

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

答案 14 :(得分:0)

您可以使用此map中的array library方法来轻松实现所需的精确目标,

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

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

答案 15 :(得分: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)
);

答案 16 :(得分:0)

我总是喜欢数组映射的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;
    });
);

答案 17 :(得分:0)

我看到它错过了明显的答案:

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行为,但它允许您重写密钥。

  • 第三个奇怪的是,如果您在返回值中忽略keyarray(1 => 'value')array(null, 'value')),则会分配新密钥,就好像{{ 1}}被使用了。这也不是$array[] = $value的常见行为,但有时它会很方便。

  • 第四个奇怪的是,如果回调函数没有返回值,或者返回map,则输出中会省略整组当前键和值,只是跳过它。这个功能完全不是null py,但如果有这样的功能,它会使map的这个功能成为极好的特技双倍。

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

  • 忽略回调返回中除null0键之外的任何其他元素。

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

    1. 分配了元素的新键
    2. null用作其值
警告:
请记住,这最后一个功能只是以前功能的残留,它可能完全没用。非常不鼓励依赖此功能,因为此功能将随机已弃用并在将来的版本中意外更改。

注意:
null不同,传递给array_map的所有非数组参数(第一个回调参数除外)都以静默方式转换为数组。

实施例:
array_map_assoc