このようなことをする方法はありますか?
$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"
}
キーを処理しないため、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"
// }
これはおそらく最も短く、最も簡単な理由です。
$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')
)
これが私の非常に単純なPHP 5.5互換ソリューションです。
function array_map_assoc(callable $f, array $a) {
return array_column(array_map($f, array_keys($a), $a), 1, 0);
}
あなたが提供する呼び出し可能オブジェクトはそれ自身2つの値、すなわち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"
}
同じマッピング関数で、異なる配列でこの関数を何度も使用する必要がある場合は、 部分関数の適用 ( ' カリー化 'に関連した)を実行できます。呼び出し時にのみデータ配列を渡すことができます。
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
は以前と同じであるため、どちらでも同じ出力が生成されます。
注:マップされた関数が2つの異なる入力に対して同じキーを返す場合、後のキーに関連付けられた値はwin。array_map_assoc
の入力配列と出力結果を逆にして、以前のキーが勝つようにします。 (私の例で返されたキーはソース配列のキーを組み込んでいるので衝突することはできません。これは順番に一意である必要があります。)
以下は上記の変形で、論理的なものもありますが、PHP 5.6が必要です。
function array_map_assoc(callable $f, array $a) {
return array_merge(...array_map($f, array_keys($a), $a));
}
この変形では、あなたが提供した(データ配列がマッピングされている関数)代わりに1行の連想配列、すなわちreturn [key => value]
を返すべきです。呼び出し可能オブジェクトをマッピングした結果は、単純に解凍されてarray_merge
に渡されます。以前と同様に、重複したキーを返すと、後の値が勝ちになります。
n.b Alex83690はコメントの中で、ここで
array_replace
の代わりにarray_merge
を使うことは整数キーを保存するであろうと述べました。array_replace
は入力配列を変更しないので、機能コードにとって安全です。
PHP 5.3から5.5までの場合は、以下は同等です。キーを保持しながら、array_reduce
とバイナリの+
配列演算子を使用して、結果として得られる2次元配列を1次元配列に変換します。
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));
つまり、答えはNOです。array_keys
を呼び出さないようにすることはできませんが、array_keys
が呼び出される場所を上位に要約することはできます。十分に良いかもしれません。
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)
)
);
これが私のプロジェクトでの実装です。
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));
}
YaLinqo library *はこの種の作業に非常に適しています。これは.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
呼び出しを削除できます。
*私が開発した
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);
「手動ループ」とは、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);
}
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))
バージョン5.6以降を使用して、問題にさらに別の解決策を追加します。それがすでに優れた解決策よりも効率的かどうかわからない(おそらくそうではない)が、私にとっては読むのが簡単だ:
$myArray = [
"key0" => 0,
"key1" => 1,
"key2" => 2
];
array_combine(
array_keys($myArray),
array_map(
function ($intVal) {
return strval($intVal);
},
$myArray
)
);
array_map
で関数の例としてstrval()
を使うと、これは生成するでしょう:
array(3) {
["key0"]=>
string(1) "0"
["key1"]=>
string(1) "1"
["key2"]=>
string(1) "2"
}
私がこれを理解するのがとても簡単だと思うのは私だけではないと思います。 array_combine
はキーの配列と値の配列からkey => value
配列を作成します、残りはかなり自明です。
ここを見てください!簡単な解決策があります!
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));
私はこのようなことをしたいと思います:
<?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"
}
私はいつも配列マップの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;
});
);
私はそれが明白な答えを欠いているのを見ます:
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
を省略すると(array(1 => 'value')
またはarray(null, 'value')
のいずれかで)、$array[] = $value
が使用されているかのように新しいキーが割り当てられます。それはmap
の一般的な振る舞いでもありません、それでもそれは時々便利になる、と私は思います。
第4の奇妙なことは、コールバック関数が値を返さない、またはnull
を返す場合、現在のキーと値のセット全体が出力から省略される場合、それは単にスキップされるということです。この機能はまったくunmap
pyですが、そのような機能がある場合は、この機能がarray_filter_assoc
の優れたスタントダブルになります。
コールバックの戻り値で2番目の要素(1 => ...
)(valueの部分)を省略すると、実際の値の代わりにnull
が使用されます。
コールバックのリターンにキー0
と1
を持つもの以外の他の要素は無視されます。
そして最後に、もしlambdaがnull
またはarray以外の値を返す場合、それはkeyとvalueの両方が省略されたかのように扱われます。
null
が使用されています警告:
この最後の機能は以前の機能の単なる残余であり、それはおそらく完全に役に立たないことに注意してください。この機能はランダムになるため、この機能に頼ることはお勧めできません。 廃止予定 将来のリリースでは予期せず変更されました。
注意:array_map
とは異なり、最初のコールバックパラメータを除いて、array_map_assoc
に渡されるすべての非配列パラメータは、暗黙的に配列にキャストされます。
例:// TODO: examples, anyone?
キーを保存せずにこれを行う別の方法は、次のとおりです。
$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 をこの array library から使用すると、次のように簡単に正確に目的を達成できます。
Arr::map($test_array, function($a, $b) { return "$a loves $b"; });
また、キーを保存し、新しい配列を返します。もちろん、ニーズに合わせていくつかの異なるモードを返します。