#php #arrays #loops #dictionary #iteration
#php #массивы #циклы #словарь #итерация
Вопрос:
Я использую карту в php следующим образом:
function func($v) {
return $v * 2;
}
$values = array(4, 6, 3);
$mapped = array_map(func, $values);
var_dump($mapped);
Возможно ли получить индекс значения в функции?
Также — если я пишу код, которому нужен индекс, должен ли я использовать цикл for вместо map?
Ответ №1:
Конечно, вы можете, с помощью 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);
Комментарии:
1. @Gordon да, вы можете предоставить
array_map()
произвольное количество аргументов 🙂2. Это очень рискованный подход, поскольку PHP не гарантирует, что ключи, возвращаемые
array_keys
, останутся в том же порядке, что и в исходном массиве. Таким образом, вы можете в конечном итоге сопоставить ключи с неправильными значениями. Безопасный подход заключается в том, чтобы использовать толькоarray_keys
в качестве второго аргументаarray_map
, а затем передать массив для закрытия с помощьюuse
инструкции.3. Честно говоря, я не понимаю, почему в PHP нет функции map, которая предоставляет ключ каждого элемента в качестве второго параметра обратного вызова.
4. @TimBezhashvyly Не могли бы вы, пожалуйста, дополнить это ссылкой на документацию PHP? php.net/manual/en/function. array-keys.php Ничего не говорит о том, что порядок не гарантируется.
5. Я согласен, что в этой ситуации лучше не использовать array_keys. Вместо этого мы можем получить индексы, используя: range(0, count($array) -1)
Ответ №2:
При отображении анонимной функции поверх анонимного массива невозможно получить доступ к ключам:
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) { /* ... */ });
Ответ №3:
Нет способа получить доступ к индексу в array_map
обратном вызове. Если вы работаете с последовательными числовыми индексами, то можно использовать увеличивающуюся статическую переменную:
$values = ["one", "two", "three"];
$mapped = array_map(function ($value) {
static $i = 0;
$result = "Index: $i, Value: $value";
$i ;
return $result;
}, $values);
print_r($mapped);
В результате:
Array
(
[0] => Index: 0, Value: one
[1] => Index: 1, Value: two
[2] => Index: 2, Value: three
)
При использовании этого подхода важно использовать анонимную функцию в качестве обратного вызова и никогда не использовать повторно эту анонимную функцию, чтобы избежать ссылки на ту же статическую переменную за пределами array_map .
Ответ №4:
Это немного старая тема, но, как и многие из вас, я использую array_keys
:
array_map(function($id, $name) {
print '<option value="'.$id.'">'.$name.'</option>';
}, array_keys($array), array_values($array));
Редактировать: Вместо use
ключевого слова вы можете добавить два массива во второй параметр вашей arrray_map
функции. Я думаю, никаких пояснений не требуется, код довольно прост.
Комментарии:
1.
array_values($array)
может быть безопасно$array
. Если вы не собираетесь использоватьarray_map()
‘sreturn
, то вам, вероятно, не следует вызыватьarray_map()
. Скорее всего, этоarray_walk()
лучше подходит.
Ответ №5:
Очень просто:
Только функция 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)
Ответ №6:
Как упоминалось в ранее опубликованных ответах, когда ключи и значения необходимы в теле функции обратного вызова, проще всего передать массив ключей, а затем получить доступ к значениям для этих ключей через исходный массив.
В более старых версиях, которые не имеют функций со стрелками, используйте use()
или global
(я рекомендую первое), чтобы разрешить доступ к исходному массиву из обратного вызова.
В современном PHP (7.4 ) используйте синтаксис функции arrow. (Демо)
$values = [4, 6, 3];
var_export(
array_map(
fn($k) => "$k: " . $values[$k] * 2,
array_keys($values)
)
);
Также нет ничего постыдного в использовании foreach()
и изменении значений по ссылке. (Демо)
$values = [4, 6, 3];
foreach ($values as $k => amp;$v) {
$v = "$k: " . $v * 2;
}
var_export($values);
Ответ №7:
Вы можете создать свою собственную функцию map с помощью 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
Ответ №8:
Для быстрого и открытого решения (без удвоения массива с помощью array_keys и подобных):
/**
* Array map alternative to work with values and keys of single array.
*
* Callable receives $value and $index of $sourceArray as arguments
* If keys are not preserved via $preserveKeys - $keyCallback can be used to determinate key
*
* @param array $sourceArray
* @param callable|null $valueCallback
* @param callable|null $keyCallback
* @param bool $preserveKeys
* @return array
*/
function array_map_indexed(
array $sourceArray,
?callable $valueCallback = null,
?callable $keyCallback = null,
bool $preserveKeys = true
): array {
$newArray = [];
foreach ($sourceArray as $key => $value) {
if ($preserveKeys) {
$newArray[$keyCallback ? $keyCallback($value, $key) : $key] = $valueCallback
? $valueCallback($value, $key)
: $value;
} else {
$newArray[] = $valueCallback
? $valueCallback($value, $key)
: $value;
}
}
return $newArray;
}
Примеры использования:
$result = array_map_indexed(
[
'a' => 'aValue',
'b' => 'bValue',
],
function($value, $index) {
return [$value, $index];
},
);
//Array ( [a] => Array ( [0] => aValue [1] => a ) [b] => Array ( [0] => bValue [1] => b ) )
$result = array_map_indexed(
[
'a' => 'aValue',
'b' => 'bValue',
],
function($value, $index) {
return $index.$value;
},
null,
false
);
//Array ( [0] => aaValue [1] => bbValue )
$result = array_map_indexed(
[
'a' => 'aValue',
'b' => 'bValue',
],
null,
function($value, $index) {
return $value === 'aValue' ? 'specificKey' : $index;
},
);
//Array ( [specificKey] => aValue [b] => bValue )
Ответ №9:
Вы можете сгенерировать свой собственный индекс для использования с array_map:
function func($v, $index) {
return $v * 2;
}
$values = array(4, 6, 3);
$valuesIndex = range(0, count($values) - 1);
$mapped = array_map(func, $values, $valuesIndex);
var_dump($mapped);
Как показано выше, вы можете безопасно создать массив чисел от 0 до длины вашего массива, такой же, как индекс.
Поместите этот второй массив в array_map и используйте его значения в качестве индекса в вашей функции.
Комментарии:
1. Я не вижу, где определена
func
константа. Вы не тестировали этот код. Пожалуйста, протестируйте свои фрагменты на 3v4l.org перед публикацией в Stack Overflow.2. Вызов двух функций (
range(0, count($values) - 1)
) для генерации данных, которые уже сгенерированы, не является хорошей идеей. Просто получите доступ к тому, что уже доступно с помощьюarray_keys()
.
Ответ №10:
Вы можете использовать функцию array_keys() для получения ключей массива, а затем перебирать эти ключи с помощью цикла foreach.
$keys = array_keys($array);
foreach($keys as $key){
// do something with $key
}
Комментарии:
1. Вы правы, но это не совсем то, что задает OP (вопрос о
array_map
)