PHP: Kann ich den Index in einer array_map-Funktion abrufen?

84

Ich benutze eine Karte in PHP wie folgt:

function func($v) {
    return $v * 2;
}

$values = array(4, 6, 3);
$mapped = array_map(func, $values);
var_dump($mapped);

Ist es möglich, den Index des Wertes in der Funktion zu erhalten?

Außerdem - wenn ich Code schreibe, der den Index benötigt, sollte ich eine for-Schleife anstelle einer Karte verwenden?

Ollie Glass
quelle

Antworten:

209

Sicher können Sie mit Hilfe von array_keys():

function func($v, $k)
{
    // key is now $k
    return $v * 2;
}

$values = array(4, 6, 3);
$mapped = array_map('func', $values, array_keys($values));
var_dump($mapped);
Aron Rotteveel
quelle
20
Coole Antwort, wusste nicht, dass Sie zusätzliche Parameter an eine array_map () ped-Methode übergeben können. Lerne jeden Tag etwas Neues!
GordonM
1
@ Gordon ja, Sie können array_map()mit einer beliebigen Anzahl von Argumenten liefern :)
Aron Rotteveel
13
Dies ist ein sehr riskanter Ansatz, da PHP nicht garantiert, dass die von zurückgegebenen Schlüssel array_keysin derselben Reihenfolge wie im ursprünglichen Array bleiben. Daher könnten Sie Schlüssel falschen Werten zuordnen. Der sichere Ansatz besteht darin, nur array_keysdas zweite Argument von zu verwenden array_mapund das Array dann mit der useAnweisung an den Abschluss zu übergeben .
user487772
12
Ich verstehe ehrlich gesagt nicht, warum PHP keine Kartenfunktion hat, die den Schlüssel jedes Elements als zweiten Parameter des Rückrufs liefert.
Grippe
1
@flu PHP hat sich nicht ohne Grund den Titel einer schlechten Sprache verdient.
xZero
9

Wenn Sie eine anonyme Funktion einem anonymen Array zuordnen, können Sie nicht auf die Schlüssel zugreifen:

array_map(
    function($val) use ($foo) { /* ... */ },
    array(key1 => val1,
          key2 => val2,
          /* ... */));

array_reduce erhält auch keinen Zugriff auf die Schlüssel. array_walk kann auf Schlüssel zugreifen, das Array wird jedoch als Referenz übergeben, was eine Indirektionsebene erfordert.

Einige Lösungen sind:

Array von Paaren

Das ist schlecht, da wir das ursprüngliche Array ändern. Außerdem nehmen die Boilerplate-Aufrufe "array ()" linear mit der Länge des Arrays zu:

array_map(
    function($pair) use ($foo) {
        list($key, $val) = $pair;
        /* ... */
    },
    array(array(key1, val1),
          array(key2, val2),
          /* ... */));

Temporäre Variable

Wir arbeiten mit dem ursprünglichen Array, und die Boilerplate ist konstant, aber wir können eine vorhandene Variable leicht blockieren:

$i_hope_this_does_not_conflict = array(key1 => val1,
                                       key2 => val2,
                                       /* ... */);
array_map(
    function($key, $val) use ($foo) { /* ... */ },
    array_keys($i_hope_this_does_not_conflict),
    $i_hope_this_does_not_conflict);
unset($i_hope_this_does_not_conflict);

One-Shot-Funktion

Wir können den Funktionsumfang verwenden, um das Überfallen vorhandener Namen zu verhindern, müssen jedoch eine zusätzliche Ebene für "Verwendung" hinzufügen:

call_user_func(
    function($arr) use ($foo) {
        return array_map(function($key, $val) use ($foo) { /* ... */ },
                         array_keys($arr),
                         $arr);
    },
    array(key1 => val1,
          key2 => val2,
          /* ... */));

One-Shot-Funktion mit mehreren Argumenten

Wir definieren die Funktion, die wir zuordnen, im ursprünglichen Bereich, um zu verhindern, dass das Boilerplate "verwendet" wird:

call_user_func(
    function($f, $arr) {
        return array_map($f, array_keys($arr), $arr);
    },
    function($key, $val) use ($foo) { /* ... */ },
    array(key1 => val1,
          key2 => val2,
          /* ... */));

Neue Funktion

Das Interessante ist, dass unsere letzte One-Shot-Funktion eine schöne, generische Signatur hat und Array_Map sehr ähnlich sieht. Vielleicht möchten wir diesem einen Namen geben und ihn wiederverwenden:

function array_mapk($f, $arr) {
    return array_map($f, array_keys($arr), $arr);
}

Unser Anwendungscode lautet dann:

array_mapk(
    function($key, $val) use ($foo) { /* ... */ },
    array(key1 => val1,
          key2 => val2,
          /* ... */));

Indirekter Array-Spaziergang

Beim Schreiben des obigen Artikels habe ich array_walk ignoriert, da das Argument als Referenz übergeben werden muss. Inzwischen habe ich jedoch festgestellt, dass es einfach ist, dies mit call_user_func zu umgehen. Ich denke, dies ist die bisher beste Version:

call_user_func(
    'array_walk',
    array(key1 => val1,
          key2 => val2,
          /* ... */),
    function($val, $key) use ($foo) { /* ... */ });
Warbo
quelle
0

Sehr einfach:

Nur array_map Funktion: hat keinen Indexschlüssel!

 $params = [4,6,2,11,20];

 $data = array_map(function($v) { return ":id{$v}";}, $params);

 array (size=5)
  0 => string ':id4' (length=4)
  1 => string ':id6' (length=4)
  2 => string ':id2' (length=4)
  3 => string ':id11' (length=5)
  4 => string ':id20' (length=5)

Kombinieren Sie jetzt mit array_keys:

$data = array_map(
    function($k) use ($params) { return ":id{$k}_${params[$k]}"; },
    array_keys($params)
 );

array (size=5)
  0 => string ':id0_4' (length=6)
  1 => string ':id1_6' (length=6)
  2 => string ':id2_2' (length=6)
  3 => string ':id3_11' (length=7)
  4 => string ':id4_20' (length=7)
Fábio Zangirolami
quelle