it-swarm.com.de

PHP array_map einschließlich Schlüssel

Gibt es eine Möglichkeit, so etwas zu tun:

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

Statt array_keys und array_values aufzurufen, die $test_array-Variable direkt übergeben?

Die gewünschte Ausgabe ist:

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

Nicht bei array_map, da Schlüssel nicht verarbeitet werden können.

array_walk macht:

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

Das als Parameter angegebene Array wird jedoch geändert, sodass die Programmierung nicht genau funktioniert (da die Frage so markiert wurde). Wie im Kommentar dargelegt, werden dadurch nur die Werte des Arrays geändert, sodass die Schlüssel nicht den von Ihnen in der Frage angegebenen Werten entsprechen.

Sie könnten eine Funktion schreiben, die die Punkte über sich selbst korrigiert, wenn Sie dies wünschen:

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"
// }
146
eis

Dies ist wahrscheinlich das kürzeste und am einfachsten zu überlegende Thema:

$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')
)
108
Man Personson

Hier ist meine sehr einfache PHP 5.5-kompatible Lösung:

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

Die von Ihnen angegebene Callable sollte selbst ein Array mit zwei Werten zurückgeben, d. H. return [key, value]. Der innere Aufruf von array_map erzeugt daher ein Array von Arrays. Dieses wird dann durch array_column in ein eindimensionales Array zurückkonvertiert.

Verwendungszweck

$ordinals = [
    'first' => '1st',
    'second' => '2nd',
    'third' => '3rd',
];

$func = function ($k, $v) {
    return ['new ' . $k, 'new ' . $v];
};

var_dump(array_map_assoc($func, $ordinals));

Ausgabe

array(3) {
  ["new first"]=>
  string(7) "new 1st"
  ["new second"]=>
  string(7) "new 2nd"
  ["new third"]=>
  string(7) "new 3rd"
}

Teilanwendung

Für den Fall, dass Sie die Funktion mehrmals mit verschiedenen Arrays, aber mit derselben Mapping-Funktion verwenden müssen, können Sie die Funktion Teilfunktionsanwendung (verwandt mit ' currying ') ausführen, die nur die Übergabe zulässt das Datenfeld beim Aufruf:

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

Welches die gleiche Ausgabe erzeugt, sind $func und $ordinals wie zuvor.

HINWEIS: Wenn Ihre zugeordnete Funktion den gleichen Schlüssel für zwei verschiedene Eingaben zurückgibt, gewinnt der mit dem späteren Schlüssel verknüpfte Wert. Kehren Sie das Eingabefeld und das Ausgabeergebnis von array_map_assoc um, damit frühere Schlüssel gewinnen können. (Die zurückgegebenen Schlüssel in meinem Beispiel können nicht kollidieren, da sie den Schlüssel des Quell-Arrays enthalten, der wiederum eindeutig sein muss.)


Alternative

Das Folgende ist eine Variante des Obenstehenden, die sich für einige als logischer erweisen könnte, aber PHP 5.6 erfordert:

function array_map_assoc(callable $f, array $a) {
    return array_merge(...array_map($f, array_keys($a), $a));
}

In dieser Variante sollte Ihre bereitgestellte Funktion (über die das Datenarray abgebildet wird) stattdessen ein assoziatives Array mit einer Zeile zurückgeben, d. H. return [key => value]. Das Ergebnis der Zuordnung der aufrufbaren Daten wird dann einfach ausgepackt und an array_merge übergeben. Die Rückgabe eines doppelten Schlüssels führt dazu, dass spätere Werte gewonnen werden.

n.b. Alex83690 hat in einem Kommentar darauf hingewiesen, dass die Verwendung von array_replace hier anstelle von array_merge Integer-Schlüssel erhalten würde. array_replace ändert das Eingabearray nicht, ist also für Funktionscode sicher.

Wenn Sie sich auf PHP 5.3 bis 5.5 befinden, ist das Folgende gleichwertig. Es verwendet array_reduce und den binären + Array-Operator, um das resultierende zweidimensionale Array in ein eindimensionales Array zu konvertieren, wobei die Schlüssel erhalten bleiben:

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;
    }, []);
}

Verwendungszweck

Beide Varianten würden also verwendet werden:

$ordinals = [
    'first' => '1st',
    'second' => '2nd',
    'third' => '3rd',
];

$func = function ($k, $v) {
    return ['new ' . $k => 'new ' . $v];
};

var_dump(array_map_assoc($func, $ordinals));

Beachten Sie den => anstelle von , in $func.

Die Ausgabe ist dieselbe wie zuvor, und jede kann auf dieselbe Weise wie zuvor angewendet werden.


 Zusammenfassung

Das Ziel der ursprünglichen Frage ist es, den Aufruf des Anrufs so einfach wie möglich zu machen, jedoch mit einer komplizierteren Funktion, die aufgerufen wird. insbesondere die Möglichkeit, das Datenarray als einzelnes Argument zu übergeben, ohne die Schlüssel und Werte zu teilen. Verwenden Sie die zu Beginn dieser Antwort bereitgestellte Funktion:

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

Oder nur für diese Frage können wir die array_map_assoc()-Funktion vereinfachen, die die Ausgabeschlüssel entfernt, da die Frage nicht nach ihnen fragt:

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

Die Antwort ist also NEIN, Sie können nicht array_keys aufrufen, aber Sie können die Stelle, an der array_keys aufgerufen wird, in eine übergeordnete Funktion aufrufen, was vielleicht gut genug ist.

44
Nicholas Shanks

Mit PHP5.3 oder höher:

$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

So habe ich das in meinem Projekt umgesetzt.

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

YaLinqo library * eignet sich gut für diese Art von Aufgaben. Es ist ein LINQ-Port von .NET, der vollständig Werte und Schlüssel in allen Rückrufen unterstützt und SQL ähnelt. Zum Beispiel:

$mapped_array = from($test_array)
    ->select(function ($v, $k) { return "$k loves $v"; })
    ->toArray();

oder nur:

$mapped_iterator = from($test_array)->select('"$k loves $v"');

Hier ist '"$k loves $v"' eine Abkürzung für die vollständige Schließsyntax, die diese Bibliothek unterstützt. toArray() ist am Ende optional. Die Methodenkette gibt einen Iterator zurück. Wenn das Ergebnis nur mit foreach durchlaufen werden muss, kann der Aufruf toArray entfernt werden.

* von mir entwickelt

2
Athari

Basierend auf der Antwort von eis habe ich Folgendes getan, um zu vermeiden, dass das ursprüngliche Array beschädigt wird:

$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

Mit "manueller Schleife" wollte ich eine benutzerdefinierte Funktion schreiben, die foreach verwendet. Dies gibt ein neues Array wie array_map do zurück, da $array aufgrund des Funktionsumfangs eine Kopie ist - keine Referenz:

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

Ihre Technik, die array_map mit array_keys verwendet, scheint jedoch einfacher und leistungsfähiger zu sein, da Sie null als Rückruf verwenden können, um die Schlüssel-Wert-Paare zurückzugeben:

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

Ich habe diese Funktion gemacht, basierend auf eis Antwort :

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

Beispiel:

$test_array = array("first_key" => "first_value", 
                "second_key" => "second_value");

var_dump(array_map_(function($key, $value){
    return $key . " loves " . $value;
}, $arr));

Ausgabe:

array (
  'first_key' => 'first_key loves first_value,
  'second_key' => 'second_key loves second_value',
)

Natürlich können Sie array_values verwenden, um genau das zurückzugeben, was das OP wünscht.

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

Schau hier! Es gibt eine triviale Lösung!

function array_map2(callable $f, array $a)
{
    return array_map($f, array_keys($a), $a);
}

Wie in der Frage angegeben, verfügt array_mapbereits über genau die erforderliche Funktionalität. Die anderen Antworten hier sind sehr kompliziert: array_walk ist nicht funktionsfähig.

Verwendungszweck  

Genau so, wie Sie es von Ihrem Beispiel erwarten würden:

$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

Ich würde so etwas machen:

<?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",
)));

?>

Ergebnisse:

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

Ich füge dem Problem mit Version 5.6 oder neuer eine weitere Lösung hinzu. Ich weiß nicht, ob es effizienter ist als die bereits guten Lösungen (wahrscheinlich nicht), aber für mich ist es einfacher zu lesen:

$myArray = [
    "key0" => 0,
    "key1" => 1,
    "key2" => 2
];

array_combine(
    array_keys($myArray),
    array_map(
        function ($intVal) {
            return strval($intVal);
        },
        $myArray
    )
);

Bei Verwendung von strval() als Beispielfunktion im array_map wird Folgendes generiert:

array(3) {
  ["key0"]=>
  string(1) "0"
  ["key1"]=>
  string(1) "1"
  ["key2"]=>
  string(1) "2"
}

Ich hoffe, dass ich nicht der einzige bin, der das ziemlich einfach fassen kannarray_combine erstellt ein key => value-Array aus einem Array von Schlüsseln und einem Array von Werten.

0
Francesco D.M.

Ich mag immer die Javascript-Variante von Array-Map. Die einfachste Version davon wäre:

/**
 * @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;
}

Jetzt können Sie einfach eine Callback-Funktion übergeben, wie Sie die Werte erstellen.

$testArray = [
    "first_key" => "first_value", 
    "second_key" => "second_value"
];

var_dump(
    arrayMap($testArray, function($value, $key) {
        return $key . ' loves ' . $value;
    });
);
0
blablabla

Ich sehe, dass die offensichtliche Antwort fehlt: 

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

Funktioniert genau wie array_map. Fast.

Eigentlich ist es nicht nur map, wie Sie es aus anderen Sprachen kennen. Php ist sehr seltsam und erfordert daher einige sehr seltsame Benutzerfunktionen, denn wir wollen unseren genau gebrochenen worse is better-Ansatz nicht brechen.

Wirklich ist es überhaupt nicht map. Trotzdem ist es immer noch sehr nützlich.

  • Der erste offensichtliche Unterschied zu array_map besteht darin, dass der Callback die Ausgabe von each() aus jedem Eingangsarray und nicht nur aus dem Wert entnimmt. Sie können immer noch mehrere Arrays auf einmal durchlaufen.

  • Der zweite Unterschied ist die Art und Weise, wie der Schlüssel behandelt wird, nachdem er vom Rückruf zurückgegeben wurde. Der Rückgabewert der Callback-Funktion sollte array('new_key', 'new_value') sein. Schlüssel können und werden geändert werden, dieselben Schlüssel können sogar dazu führen, dass der vorherige Wert überschrieben wird, wenn derselbe Schlüssel zurückgegeben wird. Dies ist kein allgemeines map-Verhalten, jedoch können Sie Schlüssel erneut schreiben.

  • Die dritte seltsame Sache ist, wenn Sie key im Rückgabewert (entweder durch array(1 => 'value') oder array(null, 'value')) weglassen, wird ein neuer Schlüssel zugewiesen, als ob $array[] = $value verwendet würde. Das ist auch nicht das übliche Verhalten von map, aber manchmal ist es praktisch, denke ich.

  • Viertens komisch ist, wenn die Callback-Funktion keinen Wert zurückgibt oder null zurückgibt, der gesamte Satz aktueller Schlüssel und Werte aus der Ausgabe ausgelassen wird, wird er einfach übersprungen. Diese Funktion ist völlig unmappy, würde jedoch diese Funktion zu einem hervorragenden Stunt-Double für array_filter_assoc machen, wenn es eine solche Funktion gibt.

  • Wenn Sie das zweite Element (1 => ...) (der value part) in der Rückgabe des Callbacks weglassen, wird null anstelle des reellen Werts verwendet.

  • Alle anderen Elemente außer den mit den Schlüsseln 0 und 1 im Rückruf des Rückrufs werden ignoriert.

  • Und wenn Lambda irgendeinen Wert außer null oder Array zurückgibt, wird es so behandelt, als würden sowohl Schlüssel als auch Wert weggelassen. 

    1. neuer Schlüssel für Element wird zugewiesen
    2. null wird als Wert verwendet 
WARNUNG:
Denken Sie daran, dass dieses letzte Feature nur ein Rest der vorherigen Features ist und wahrscheinlich völlig nutzlos ist. Es wird dringend davon abgeraten, sich auf diese Funktion zu verlassen, da diese Funktion zufällig sein wird veraltet und in zukünftigen Releases unerwartet geändert.

HINWEIS:
Im Gegensatz zu array_map werden alle Nicht-Array-Parameter, die an array_map_assoc übergeben werden, mit Ausnahme des ersten Rückrufparameters, unbemerkt in Arrays umgewandelt.

BEISPIELE
// TODO: examples, anyone?

0
enrey

Eine andere Möglichkeit, dies zu erreichen, ohne die Schlüssel zu erhalten:

$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

Sie können die map -Methode aus dieser array library verwenden, um genau das zu erreichen, was Sie möchten, und zwar so einfach wie:

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

außerdem werden Schlüssel beibehalten und neue Arrays zurückgegeben, ganz zu schweigen von einigen verschiedenen Modi, die Ihren Anforderungen entsprechen.

0
Minwork