PHP: Могу ли я получить индекс в функции array_map?

87

Я использую карту в php так:

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

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

Можно ли получить индекс значения в функции?

Кроме того, если я пишу код, которому нужен индекс, следует ли использовать цикл for вместо карты?

Олли Гласс
источник

Ответы:

215

Конечно, можно, с помощью 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);
Арон Роттевил
источник
20
Классный ответ, я не понимал, что вы можете передать дополнительные параметры в метод ped array_map (). Узнавайте что-то новое каждый день!
GordonM
1
@Gordon: да, вы можете привести array_map()произвольное количество аргументов :)
Арон Роттевил
13
Это очень рискованный подход, поскольку PHP не гарантирует, что ключи, возвращаемые функцией array_keys, останутся в том же порядке, что и в исходном массиве. Таким образом, вы можете сопоставить ключи с неправильными значениями. Безопасный подход - использовать только array_keysв качестве второго аргумента, array_mapа затем передать массив для закрытия с помощью useоператора.
user487772 02
12
Честно говоря, я не понимаю, почему в PHP нет функции карты, которая предоставляет ключ каждого элемента в качестве второго параметра обратного вызова.
грипп
1
@flu PHP не заслужил звание ругательства без причины.
xZero
9

При отображении анонимной функции на анонимный массив нет возможности получить доступ к ключам:

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

array_reduce также не получает доступа к ключам. array_walk может обращаться к ключам, но массив передается по ссылке, что требует уровня косвенного обращения.

Вот некоторые решения:

Массив пар

Это плохо, поскольку мы меняем исходный массив. Кроме того, стандартные вызовы array () линейно увеличиваются с увеличением длины массива:

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

Временная переменная

Мы работаем с исходным массивом, и шаблон постоянен, но мы можем легко затереть существующую переменную:

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

Одноразовая функция

Мы можем использовать область видимости функции, чтобы предотвратить затирание существующих имен, но должны добавить дополнительный уровень «использования»:

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

Одноразовая функция с несколькими аргументами

Мы определяем функцию, которую мы отображаем в исходной области видимости, чтобы предотвратить "использование" шаблона):

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

Новая функция

Интересно отметить, что наша последняя одноразовая функция имеет хорошую общую сигнатуру и очень похожа на array_map. Мы могли бы захотеть дать этому имя и использовать его повторно:

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

Код нашего приложения становится таким:

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

Непрямая прогулка по массиву

При написании вышесказанного я проигнорировал array_walk, поскольку он требует, чтобы его аргумент передавался по ссылке; однако с тех пор я понял, что это легко обойти, используя call_user_func. Я думаю, что это лучшая версия на данный момент:

call_user_func(
    'array_walk',
    array(key1 => val1,
          key2 => val2,
          /* ... */),
    function($val, $key) use ($foo) { /* ... */ });
Варбо
источник
1

Очень просто:

Только функция array_map: не имеет индексного ключа!

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

Теперь объедините с 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)
Фабио Зангиролами
источник
0

Вы можете создать свою собственную функцию карты, используя foreach:

<?php

function myCallback($key, $val)
{
    var_dump("myCallback - key: $key, val: $val");
    return $val * 2;
}

function foreachMap($callback, $givenArray) {
    $result = [];
    foreach ($givenArray as $key=>$val) {
        $result[$key] = $callback($key, $val);
    }
    return $result;
}

$values = array(4, 6, 3);
$mapped = foreachMap('myCallback', $values);
var_dump($mapped);

попробуйте: https://3v4l.org/pmFlB

домис86
источник