キーを含むPHPのarray_map


208

このようなことをする方法はありますか?

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

参照: この一般的な問題に対する対照的なアプローチについては、stackoverflow.com
search?q

回答:


206

キーを処理しないため、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"
// }

この場合を除いて$a = "$b loves $a"、OPの必要な出力に一致させる必要があります。
cmbuckley 2012年

1
正しい、変更された:)彼らがarray_mapをarray_walkとどのように異なっているかは素晴らしいことです。
EIS

よかった、ありがとう。元の配列をいじり避けるために、ここでは(以下私の答えを見て)私が最終的にやったことだ
ホセ・トマスTocino

3
これは「関数型プログラミング」ではありarray_walk()ませんが、結果の配列を返さず、ブール値を返します。
mae 2017

@maeはい、答えも書いたように、値を返す代わりにパラメータを変更します
eis

145

これはおそらく以下の理由で最も短く簡単です。

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

15
質問は具体的には使用しないと述べたことに気づきましたarray_keys()。しかし、それはばかげた要件のように思えます。
Kevin Beal 2015

3
この質問はarray_keys()を使用した解決策を提供しました。現在の解決策よりも利点が少ない(関数の呼び出しが少ないなど)回答を提供するのはばかげています。
Chinoto Vokro

元の質問に対する答えは「いいえ」で、これが最も適切な解決策です。
usoban 2016年

65

これが私の非常にシンプルな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したがって、への内部呼び出しにより、配列の配列が生成されます。次に、これはによって1次元配列に変換され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つの異なる入力に対して同じキーを返す場合、後のキーに関連付けられた値が優先されます。入力配列と出力結果を逆に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ます。以前と同様に、重複したキーを返すと、後の値が優先されます。

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

なお、=>代わりの,の中で$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より高次の関数に抽象化できます。これで十分でしょう。


7
これは正解としてマークする必要があるようです。
eddiewould 2017

6
@eddiewouldに感謝しますが、私は約4 about年遅すぎます:)私は解決策を探してここに来て、好きなものが見つからなかったので、自分で考えました。
ニコラスシャンクス

1
私はその人になります。PHP 5.3は、この回答の日付の要件ではなくなります。私見では。
Erutan409 2018

1
最初の代替ソリューションは無効です。整数になるキーを保持するには、array_mergebyで置き換える必要array_replaceがあります。
Alex83690

1
@ Alex83690ありがとう!私は「無効」は少し誤解を招くと言いますが、整数キーがなくても問題ありません(私の場合はそうでした)
ニコラスシャンクス

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

1
要件は「array_keysとarray_valuesを呼び出す代わりに、直接$ test_array変数を渡すこと」だったと思いますが、これはarray_keysなしで使用できますか?
eisは2014年

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

非常にクリーンで、元の配列を変更しません!
Raffaele Candeliere、

4

ここを見て!簡単な解決策があります!

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

指定された質問qrrqy_keys()は#reasonsに使用してはならないため、他の回答は複雑すぎます
Brad Kent

2

「手動ループ」とは、を使用するカスタム関数を書くことを意味しましたforeacharray_map関数のスコープは$array参照ではなくコピーになるため、これはと同様に新しい配列を返します。

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

ただし、array_mapwith-with を使用するテクニックarray_keysnull、キーと値のペアを返すコールバックとして使用できるため、実際にはよりシンプルで強力です。

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

参照で配列をループすると、不気味なことが発生
janenz00

不気味ではありませんunset( $value )。定義されたスコープ内にまだ存在しているため、忘れているだけです。
aziz punjani 2012年

@azis、記事を参照して、不気味さについて冗談でした。解除を忘れると、予期しない効果が生じます。
janenz00

1
答えてくれてありがとう。でも、従来のループを使いたくないのは明らかだと思いました。
ホセ・トマスTocino

@ janenz00明確にするために編集された回答を参照してください。クリーンな変数スコープでループすることを意味しました。
ライアンブ2014年

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

2
配列の値とキーを直接array_mapに渡すよりも簡単なのはなぜですか?それは遅くて複雑です、私は利点を見ていません。
アリエル14年

1
@アリエルは、数字が大きくても遅くなるという主張を裏付けることができますか?配列を1回だけ反復する必要があるので、大きなO表記の方がはるかに高速になるはずです。ただし、複雑さについては同意します。
e's

@eis Cで一括ではなく、PHPで結果の配列を一度に1つずつ作成するため、処理が遅くなります。ただし、array_keysの呼び出しは回避されます(ただし、Cにあるため高速です)。それをベンチマークする-どちらが速いかを見てください、私は本当に確かではありませんが、通常、より多くのコード=より遅いコードです。ただし、複雑さは明らかに悪いので、ほとんどの場合、速度よりも重要です。
アリエル14

1
array_walkクロージャーでそれを参照していないため、3番目の引数をに送信する必要はありません。
Steven Lu

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_valuesOPが望むものを正確に返すために使用できます。

array_values(array_map_(function($key, $value){
    return $key . " loves " . $value;
}, $test_array))

@KevinBeal私は自分の仕事でこの関数をよく使用しています。エラーの場所を教えていただけますか?
Julio Vedovatto、2015年

2
まず、現状のコードには$arr配列タイプのチェックがcallableありarrayませんが、引数を入力ヒントとして使用し、代わりにチェックをにドロップすることができますis_callable。次に、$ valueへの割り当てを行いますが、その値は使用されません。戻り値は無視してください。3番目に、falseを返すよりも、コールバックで例外をスローする方が適切です。次に、常に有効な値を返すか、常にスローします。
Nicholas Shanks

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()最後にオプションです。メソッドチェーンはイテレータを返すため、を使用して結果を反復するだけでよい場合はforeachtoArray呼び出しを削除できます。

*私が開発


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

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ます。残りは自明です。


1

この配列ライブラリのmapメソッドを使用すると、次のように簡単に、必要なものを正確に実現できます。

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

また、キーを保持して新しい配列を返します。ニーズに合わせていくつかの異なるモードは言うまでもありません。


1
$array = [
  'category1' => 'first category',
  'category2' => 'second category',
];

$new = array_map(function($key, $value) {
  return "{$key} => {$value}";
}, array_keys($array), $array);

ソース


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

:つまり、あなたは関数合成を得る-あなたはその後、ベーク-におけるいくつかの特定のコールバック(動作が)という新しい関数を作成することができますように、あなたが書いた任意の関数の最後の引数としてデータを持つことがより有用であるh(g(f($data)))適用されf、その後、gその後、hあなたにデータ。一般に、関数プログラミングでは、固定データセットにダイバー関数を適用する関数よりも、ダイバーデータに対して同じ操作を実行する関数を使用する方が用途が広いと考えられています。
ニコラスシャンクス2018

あなたの例では、関数への引数は1つだけです。array_filter、array_reduce、JavaScriptの配列関数のように、最初の引数としてデータを置く方が簡単だと思います。
blablabla 2018

それが私のポイントです!データを最後に渡すことで、関数をカレー化(ループと特定の操作を組み合わせた新しい関数を作成)し、単一のパラメーターを指定して新しい関数を呼び出すことにより、データにそれを適用できます。このプリンシパルは、私がここでできるよりもよく説明されています。この回答では、stackoverflow.com
Nicholas Shanks、

PHPのような言語で構成関数を使用することは、この問題のより良い解決策ではありませんか?
blablabla

1
これは別の方法ですが、FPへのかなり多くの投資が必要です。たとえば、次のようになります:github.com/nickshanks/fp-php-talk/blob/master/lib.php#L24またはこれ:github.com/nickshanks/php-fp/blob /master/src/fp.php#L62
ニコラスシャンクス

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

-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()値だけではなく、すべての入力配列からの出力を受け取ることです。それでも、一度に複数の配列を反復処理できます。

  • 2番目の違いは、コールバックから返された後のキーの処理方法です。コールバック関数からの戻り値はでなければなりませんarray('new_key', 'new_value')。キーは変更される可能性があり、変更されます。同じキーが返された場合、同じキーで以前の値が上書きされることさえあります。これは一般的なmap動作ではありませんが、キーを書き換えることができます。

  • 3番目に奇妙なことは、key戻り値を(by array(1 => 'value')またはのいずれかでarray(null, 'value'))省略すると、あたかも使用されたかのように、新しいキーが割り当てられること$array[] = $valueです。それもmapの一般的な振る舞いではありませんが、時には重宝します。

  • 4つ目の奇妙な点は、コールバック関数が値を返さない場合、またはを返すnull場合、現在のキーと値のセット全体が出力から省略され、単にスキップされることです。この機能は完全にun mappyですが、この機能array_filter_assocがあった場合、この機能はの優れたスタントダブルになります。

  • コールバックの戻りで2番目の要素(1 => ...)(値の部分)を省略するとnull、実際の値の代わりにが使用されます。

  • キーのある要素01コールバックの戻り要素以外の要素は無視されます。

  • そして最後に、ラムダがnullまたは配列以外の値を返す場合、キーと値の両方が省略されているかのように扱われるため、次のようになります。

    1. 要素の新しいキーが割り当てられています
    2. null その値として使用されます
警告:
この最後の機能は以前の機能の残りであり、おそらく完全に役に立たないことに注意してください。この機能は将来のリリースでランダムに非推奨になり、予期せず変更されるため、この機能に依存することはお勧めできません。

注:
とは異なり、最初のコールバックパラメータを除いarray_maparray_map_assoc、に渡されるすべての非配列パラメータは、暗黙的に配列にキャストされます。

例:
// TODO: examples, anyone?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.