Я хочу создать функцию обратного вызова для удаления из числового массива заданных значений.
Учитывая этот массив: [ 0, 0, 0, 0, 1, 0, 3, 4, 5, 2, 0, ];
Я хочу создать вызываемую функцию для передачи в array_filter
, которая будет возвращать этот массив: [1, 0, 3, 4, 5, 2, 0, ];
. В этом ответе я видел использование array_filter: Удаление элемента из массива в PHP, но это не совсем то, что мне нужно, потому что этот параметр удалит все 0 из массива, и мне нужно сохранить те, которые не являются ведущий.
Я создал эту функцию, которая циклически обрабатывает массив и возвращает ожидаемый массив, но я хочу использовать что-то более эффективное:
function removeLeading( int $array_values[], $to_remove = 0 )
{
$is_leading = true;
$result = [];
foreach( $array_values as $value ) {
if ( ( $value == $to_remove ) && ( $is_leading == true ) ) {
// Do nothing because it is a leading $to_remove
}
else {
$result[] = $value;
$is_leading = false;
}
}
return $result;
}
Как вы относитесь к хакерским решениям? str_split(preg_replace('/0{2,}/', '', implode('', [ 0, 0, 0, 0, 1, 0, 3, 4, 5, 2, 0, ]))))
(Также предполагается, что все значения являются однозначными целыми числами, 10
и т. д. может привести к ошибке)
И Как найти первый элемент массива со значением больше X в PHP?
array_filter
не является подходящим инструментом для этой задачи, поскольку он проверяет каждый элемент массива, и наиболее эффективный алгоритм для этой задачи, вероятно, — прекратить итерацию, как только вы встретите первый ненулевой элемент.
Тогда вместо того, чтобы копировать оставшиеся элементы на выход один за другим, вы можете использовать array_slice для создания нового массива, начиная с определенного смещения.
$firstElementToKeep = 0;
foreach ( $input as $value ) {
// As soon as we find a non-zero element, stop looking
if ( $value !== 0 ) {
break;
}
// Track the offset we reach
$firstElementToKeep++;
}
$output = array_slice($input, $firstElementToKeep);
При вводе примера в вашем вопросе $firstElementToKeep
достигает 4
, который является индексом 1
, поэтому мы «разрезаем» оттуда до конца массива и получаем желаемый результат.
В большинстве случаев разница будет совершенно незаметна, но для очень большого массива может быть заметная экономия за счет меньшего количества сравнений и отсутствия необходимости изменять размер выходного массива в памяти по мере добавления новых элементов.
Привет, спасибо, отличный ответ. Мне было интересно, есть ли способ избежать использования цикла. (Я знаю, что в конце концов за кортеном всегда будет цикл), но вы знаете, когда вы делаете подобные вещи, вы используете регулярное выражение, тем не менее ваш ответ отлично справляется с задачей
@Tales Единственная возможная оптимизация, которую я вижу, — это вернуть исходный массив, если $firstElementToKeep
равно 0.
@Tales Как вы говорите, где-то внизу будет цикл, по крайней мере, до тех пор, пока вы используете структуру данных высокого уровня, такую как массив PHP, а не находите какой-то сумасшедший трюк, специфичный для процессора, с необработанной памятью. Использование чего-то вроде array_filter
переместит цикл на C, но за счет многократного переключения обратно на контекст PHP для запуска обратного вызова, что, вероятно, приведет к чистому снижению производительности. Я не могу не подчеркнуть, что это почти наверняка ненужная микрооптимизация, поэтому более важно иметь код, который легко понять (а регулярные выражения, как известно, не так!)
Да, вы можете сделать это с помощью фильтра массива. Хитрость заключается в том, чтобы накопить первые значения и вернуть false, если оно все еще равно нулю.
Это будет работать только в том случае, если вы хотите обрезать ложное значение.
$filtered = array_filter(
[0, 0, 0, 0, 1, 0, 3, 4, 5, 2, 0],
function($value) use (&$sum) { return $sum += $value === 0 ? false : $value; }
);
дает тебе
array (
4 => 1,
5 => 0,
6 => 3,
7 => 4,
8 => 5,
9 => 2,
10 => 0,
)
Но если вы хотите избавиться от исходных индексов, используйте
$filtered = [...array_filter(
[0, 0, 0, 0, 1, 0, 3, 4, 5, 2, 0],
function($value) use (&$sum) { return $sum += $value === 0 ? false : $value; }
)];
переиндексировать и сделать так
array (
0 => 1,
1 => 0,
2 => 3,
3 => 4,
4 => 5,
5 => 2,
6 => 0,
)
array_values()
.
@micmackusa Игра изменилась с выходом PHP 8.x. См. пример ответа: 3v4l.org/N8hNN PHP научился оптимизировать его посредством вызова функции.
Похоже, что упаковка пятен постоянно ухудшается до версии 8.3.
@mickmackusa Поправьте меня, если я ошибаюсь. На мой взгляд, с версии 8.3 упаковка спадов происходит быстрее, чем array_values. Я интерпретирую меньшую продолжительность как лучшую. ibb.co/3zfZGhz
Я не думаю, что мы расходимся во мнениях. Разница в производительности незначительна с версии 8.3.
Кстати, «завершающий» здесь неправильное слово — оно относится к элементам в конце массива; элементы в начале массива можно назвать «ведущими»