web-dev-qa-db-ja.com

キーを含むPHPのarray_map

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

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

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

これはおそらく最も短く、最も簡単な理由です。

$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')
)
116
Kevin Beal

これが私の非常に単純な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が呼び出される場所を上位に要約することはできます。十分に良いかもしれません。

51
Nicholas Shanks

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

これが私のプロジェクトでの実装です。

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

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呼び出しを削除できます。

*私が開発した

2
Athari

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

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))
1
Julio Vedovatto

バージョン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配列を作成します、残りはかなり自明です。

1
Francesco D.M.

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

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

私はこのようなことをしたいと思います:

<?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
Koala Yeung

私はいつも配列マップの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;
    });
);
0
blablabla

私はそれが明白な答えを欠いているのを見ます:

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を返す場合、現在のキーと値のセット全体が出力から省略される場合、それは単にスキップされるということです。この機能はまったくunmappyですが、そのような機能がある場合は、この機能がarray_filter_assocの優れたスタントダブルになります。

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

  • コールバックのリターンにキー01を持つもの以外の他の要素は無視されます。

  • そして最後に、もしlambdaがnullまたはarray以外の値を返す場合、それはkeyとvalueの両方が省略されたかのように扱われます。

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

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

例:
// TODO: examples, anyone?

0
enrey

キーを保存せずにこれを行う別の方法は、次のとおりです。

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

map をこの array library から使用すると、次のように簡単に正確に目的を達成できます。

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

また、キーを保存し、新しい配列を返します。もちろん、ニーズに合わせていくつかの異なるモードを返します。

0
Minwork