php вставить элемент в середину массива

Вставить новый элемент в массив на любой позиции в PHP

Как вставить новый элемент в массив в любой позиции, например, в середине массива?

17 ответов

Вы можете найти это немного более интуитивным. Для этого требуется только один вызов функции array_splice :

Если replacement-это только один элемент, нет необходимости помещать array() вокруг него, если элемент не является самим массивом, объектом или NULL.

функция, которая может вставлять как целочисленные, так и строковые позиции:

таким образом, вы можете вставить массивы:

нет собственной функции PHP (о которой я знаю), которая может делать именно то, что вы просили.

Я написал 2 метода, которые, по моему мнению, подходят для цели:

хотя быстрее и, вероятно, более эффективная память, это действительно подходит только там, где нет необходимости поддерживать ключи массива.

Если вам нужно поддерживать ключи, то следующее Было бы более целесообразно;

Это также рабочее решение:

на основе @Halil отличный ответ, вот простая функция, как вставить новый элемент после определенного ключа, при сохранении целых ключей:

вы можете использовать этот

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

обычно со скалярными значениями:

чтобы вставить один элемент массива в массив, не забудьте обернуть массив в массив (так как это было скалярное значение!):

в противном случае все ключи массива будут добавлены по частям.

подсказка для добавления элемента в начале массива:

если не уверен, то НЕ ИСПОЛЬЗУЙТЕ ЭТИ:

поскольку с + исходный массив будет перезаписан. (см. Источник)

решение Джея.ли идеален. Если вы хотите добавить элемент(ы) в многомерный массив, сначала добавьте одномерный массив, а затем замените его.

добавление элемента в том же формате в этот массив добавит все новые индексы массива как элементы, а не только элемент.

Примечание: добавление элементов непосредственно в многомерный массив с помощью array_splice добавит все свои индексы как элементы, а не только это пункт.

Это то, что сработало для меня для ассоциативного массива:

для вставки элементов в массив со строковыми ключами вы можете сделать что-то вроде этого:

Источник

Вставить новый элемент в массив в любой позиции в PHP

Как я могу вставить новый элемент в массив в любую позицию, например, в середину массива?

22 ответа

Вы можете найти это немного более интуитивно понятным. Требуется только один вызов функции для array_splice :

Функция, которая может вставлять как целые, так и строковые позиции:

Таким образом можно вставлять массивы:

Не существует встроенной функции PHP (о которой я знаю), которая могла бы делать именно то, что вы просили.

Я написал 2 метода, которые, как мне кажется, подходят для данной цели:

Хотя это быстрее и, вероятно, с большей эффективностью использования памяти, это действительно подходит только там, где нет необходимости поддерживать ключи массива.

Если вам действительно нужно поддерживать ключи, более подходящим будет следующее:

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

Основываясь на отличном ответе @Halil, вот простая функция, как вставить новый элемент после определенного ключа, сохраняя при этом целочисленные ключи:

Вот что у меня сработало для ассоциативного массива:

Это тоже рабочее решение:

Если не уверены, то НЕ ИСПОЛЬЗУЙТЕ ЭТО :

Потому что с + исходный массив будет перезаписан. (см. исходный код)

Решение от jay.lee идеально. Если вы хотите добавить элементы в многомерный массив, сначала добавьте одномерный массив, а затем замените его.

Добавление элемента в том же формате в этот массив добавит все новые индексы массива как элементы, а не только как элемент.

Примечание. Добавление элементов непосредственно в многомерный массив с помощью array_splice добавит все свои индексы как элементы, а не только этот элемент.

Источник

array_push

(PHP 4, PHP 5, PHP 7, PHP 8)

array_push — Добавляет один или несколько элементов в конец массива

Описание

Список параметров

Возвращаемые значения

Возвращает новое количество элементов в массиве.

Список изменений

ВерсияОписание
7.3.0Теперь эта функция может быть вызвана с одним параметром. Ранее требовалось минимум два параметра.

Примеры

Пример #1 Пример использования array_push()

Результат выполнения данного примера:

Смотрите также

User Contributed Notes 35 notes

If you’re going to use array_push() to insert a «$key» => «$value» pair into an array, it can be done using the following:

It is not necessary to use array_push.

Hope this helps someone.

Rodrigo de Aquino asserted that instead of using array_push to append to an associative array you can instead just do.

. but this is actually not true. Unlike array_push and even.

. Rodrigo’s suggestion is NOT guaranteed to append the new element to the END of the array. For instance.

$data[‘one’] = 1;
$data[‘two’] = 2;
$data[‘three’] = 3;
$data[‘four’] = 4;

. might very well result in an array that looks like this.

[ «four» => 4, «one» => 1, «three» => 3, «two» => 2 ]

I can only assume that PHP sorts the array as elements are added to make it easier for it to find a specified element by its key later. In many cases it won’t matter if the array is not stored internally in the same order you added the elements, but if, for instance, you execute a foreach on the array later, the elements may not be processed in the order you need them to be.

If you want to add elements to the END of an associative array you should use the unary array union operator (+=) instead.

$data[‘one’] = 1;
$data += [ «two» => 2 ];
$data += [ «three» => 3 ];
$data += [ «four» => 4 ];

You can also, of course, append more than one element at once.

$data[‘one’] = 1;
$data += [ «two» => 2, «three» => 3 ];
$data += [ «four» => 4 ];

. which will result in an array that looks like this.

[ «element1» => 1, «element2» => 2, «element3» => 3, «element4» => 4 ]

This is how I add all the elements from one array to another:

If you’re adding multiple values to an array in a loop, it’s faster to use array_push than repeated [] = statements that I see all the time:

$ php5 arraypush.php
X-Powered-By: PHP/5.2.5
Content-type: text/html

Adding 100k elements to array with []

Adding 100k elements to array with array_push

Adding 100k elements to array with [] 10 per iteration

Adding 100k elements to array with array_push 10 per iteration

?>
The above will output this:
Array (
[0] => a
[1] => b
[2] => c
[3] => Array (
[0] => a
[1] => b
[2] => c
)
)

There is a mistake in the note by egingell at sisna dot com 12 years ago. The tow dimensional array will output «d,e,f», not «a,b,c».

Need a real one-liner for adding an element onto a new array name?

Skylifter notes on 20-Jan-2004 that the [] empty bracket notation does not return the array count as array_push does. There’s another difference between array_push and the recommended empty bracket notation.

Empy bracket doesn’t check if a variable is an array first as array_push does. If array_push finds that a variable isn’t an array it prints a Warning message if E_ALL error reporting is on.

So array_push is safer than [], until further this is changed by the PHP developers.

If you want to preserve the keys in the array, use the following:

elegant php array combinations algorithm

A common operation when pushing a value onto a stack is to address the value at the top of the stack.

This can be done easily using the ‘end’ function:

A function which mimics push() from perl, perl lets you push an array to an array: push(@array, @array2, @array3). This function mimics that behaviour.

Add elements to an array before or after a specific index or key:

Источник

[Перевод] Массивы, срезы (и строки): Механизм ‘вставки’

Вступление

В этой публикации мы постараемся развеять все эти недоразумения. Добьемся мы этого по кусочкам объясняя как работает функция append, и почему она работает именно так и никак иначе.

Массивы

Массивы это важный кусочек языка Go, но, как и фундамент здания, он спрятан под более видимыми частями. Мы должны ввести вас в курс дела, прежде чем перейти к более интересным, мощным и заметным особенностям срезов.

Массивы не часто встретишь в программах на Go, потому что в тип массива входит его размер, что ограничивает возможности использование.

создает переменную buffer, которая содержит 256 байт. Тип переменной buffer включает в себя размер и выглядит так: [256]byte. Массив из 512 байт будет иметь тип [512]byte.

Данные связанные с массивом это просто массив элементов. Схематически, наш буфер в памяти будет иметь примерно следующий вид:

То есть, переменная содержит 256 байт данных и ничего более. Мы можем получить доступ к элементам с помощью привычного синтаксиса индексации buffer[0], buffer[1], и так до buffer[255] (индекс от 0 до 255 охватывает 256 элементов). Попытка выйти за пределы этого диапазона приведет к аварийной остановке программы.

Существует встроенная функция len, которая возвращает количество элементов массива, среза и некоторых других типов. Очевидно, что именно вернет len для массива. В нашем случае len(buffer) вернет значение 256.

Для массивов можно найти свое место применения. Например они хороши, для преобразования матриц, но наиболее частое их применение в Go это хранение срезов.

Срезы: Заголовок среза

Срезы там, где происходит что-то интересное, но перед тем как приступить к их использованию, потребуется понять их надобность и то, что-же они делают.

Срез это структура данных описывающая множественное разделение массива и хранящееся отдельно от переменной. Срез это не массив. Срез описывает часть массива.

Если мы возьмем массив buffer из предыдущего раздела, то мы можем создать срез, который будет описывать элементы от 100 до 150 (если быть точным, то от 100 до 149 включительно) путем нарезания массива:

В этом куске кода, чтобы быть точными, мы использовали полное объявление переменной. Переменная slice имеет тип []byte, читается как «срез байтов» (slice of bytes) и создана из массива buffer, путем нарезания начиная с элемента 100 (включительно) до 150 (исключительно). В более «каноническом» синтаксисе, мы бы опустили тип, который будет определен в процессе инициализации:

А внутри функции мы бы использовали короткую форму объявления:

Что же из себя представляет срез? Это не полное описание, но с этого момента думайте о срезе как о небольшой структуре, состоящей из двух элементов: длины и указателя на элемент массива. Считайте что «за кулисами» это выглядит примерно так:

Конечно, это лишь иллюстрация. Несмотря на это, из этого примера можно понять то, что структура sliceHeader недоступна программисту, а тип указателя зависит от типа элементов, однако он дает возможность понять основную идею механики работы срезов.

До сих пор мы использовали операцию нарезания массива, однако мы можем нарезать и срез:

Точно так-же как и прежде, эта операция создает новый срез, но в этом случае из элементов с 5 до 9 (включительно) относительно оригинального среза, что означает элементы с 105 по 109 оригинального массива. Базовая структура sliceHeader для переменной slice2 будет выглядеть так:

Обратите внимание, что заголовок до сих пор указывает на базовый массив, находящийся в переменной buffer.

Мы так-же можем перенарезать, что означает нарезать срез и сохранить результат в структуре нарезаемого среза. Т.е. после:

структура sliceHeader для переменной slice будет выглядеть так-же как и для переменной slice2. Вы будете часто видеть перенарезку, например для сокращения среза. В этом примере будет опущен первый и последний элемент нашего среза:

Вы можете часто слышать от опытных Go программистов о «заголовке среза», т.к. это и есть то, что хранится в переменной среза. Например, когда вы вызываете функцию которая берет срез как аргумент, такая как bytes.IndexRune, то в функцию будет передана заголовок. В этом примере:

аргумент slice будет передан в функцию IndexRune и, по факту, это лишь «заголовок среза».

Есть еще один элемент данных в «заголовке среза» о котором мы поговорим ниже, но сначала давайте взглянем на то, что значит «заголовок среза», когда вы пишете программу использующую срезы.

Передача срезов в функции

Очень важно понять то, что даже если срез содержит указатель, сам по себе он является значением. Под капотом, это структура содержащая указатель и длину. Это не указатель на структуру.

Когда мы вызываем IndexRune в предыдущем примере, она принимает копию «верхушки заголовка». Такое поведение имеет важное последствие.

Рассмотрим простую функцию:

Она делает именно то, что написано в названии, т.е. обходит все элементы среза (используя цикл for range), увеличивая его элементы.

Несмотря на то что «заголовок среза» передается в функцию, она включает указатель на элементы массива, поэтому оригинальная заголовок среза и его копия описывают один и тот-же массив. Как следствие, когда функция завершается, измененные элементы можно увидеть через исходный срез.

Аргумент функции на самом деле копия, и данный пример это показывает:

Здесь мы видим что содержимое аргумента может быть изменено через функцию, но не его заголовок. Длина сохранена в переменной slice и не изменяется в результате вызова функции, т. к. в функцию передается копия заголовка среза, а не исходный. Таким образом, если мы хотим написать функцию которая изменяет заголовок, мы должны вернуть его, как мы это сделали в примере. Переменная slice не изменяется, но возвращаемое значение имеет новую длину, которая сохранена в newSlice.

Указатели на срезы: Методы получения

Существует и другой путь написания функции которая изменяет заголовок среза, и это передача в функцию указатель на срез. Вот вариация нашего примера, для демонстрации данной возможности:

Пример может показаться немного неуклюжим, учитывая дополнительный уровень абстракции (временная переменная), но есть один случай, где вы будете применять указатели на срезы. Принято использование указателя в качестве приемника при написании метода, который изменяет срез.

Скажем, мы захотели метод который ликвидирует последний слэш. Мы можем написать его примерно так:

Если вы запустите пример, то вы увидите, что произойдет то, что требовалось, т. е. метод изменит срез.

С другой стороны, если мы захотим написать метод для path, который устанавливает верхний регистр ASCII символов (с не Английские буквами поведение не определено), то метод может оперировать значением, а не указателем, потому что значение приемника все еще указывает на нужный нам массив.

Здесь метод ToUpper использует две переменных в for range для того, чтобы использовать индекс элемента и, непосредственно, сам элемент среза. Это позволит избежать повторной записи в p[i].

Ёмкость

Рассмотрим следующую функцию, которая увеличивает срез ints на один элемент:

Посмотрим как срез растет до тех пор пока… он не растет.

Пришло время поговорить о третьем компоненте заголовка среза: его ёмкости. Помимо указателя на массив и его длины, заголовок срез содержит его ёмкость:

Поле Capacity содержит запись о том, сколько места занимает массив на самом деле – это максимальное значение, которое может достигнуть Length. Попытка увеличения среза выше его ёмкости приведет к выходу за пределы массива и вызовет экстренное завершение программы.

В этом примере создается срез

и его заголовок выглядит как:

Поле Capacity эквивалентно длине оригинального массива минус индекс элемента массива, который является первым элементом среза (в данном случае ноль). Если вы хотите узнать ёмкость среза, то используйте функцию cap:

Что если мы захотим увеличить срез больше чем его ёмкость? Мы не можем! По определению ёмкость это предел роста. Но вы можете получить тот-же результат путем создания нового массива, копирования данных и изменения среза описывающего новый массив.

Давайте начнем с выделения. Мы можем использовать функцию new для выделения большего массива и в результате большего среза, но будет проще использовать функцию make. Она выделяет новый массив и создает заголовок среза. Функция make имеет три аргумента: тип среза, начальная длина и его ёмкость, где длина массива это то, что make выделяет для данных среза. В результате этот вызов функции создает срез длиной 10 и возможностью расширения на 5 (15-10), что вы можете увидеть запустив это:

Этот фрагмент удваивает ёмкость нашего int среза, но оставляет такую-же длину:

После этого срез имеет гораздо больше места для роста перед тем, как ему потребуется еще одно перераспределение.

При создании срезов часто бывает что длина и ёмкость это одно и тоже. Функция make имеет сокращенный вариант. Длина по умолчанию становится ёмкостью, поэтому вы можете указать их в одном значении. После

у среза gophers длина и ёмкость будет равна 10.

Копирование

После того как мы удвоили ёмкость нашего среза в предыдущем разделе, мы переписали цикл для копирования старых данных в новый срез. В Go есть встроенная функция copy, которая упрощает эту задачу. Её аргументы это два среза и она копирует данные из правого в левый срез. Вот наш пример переписанный на использование copy:

Функция copy умная. Она копирует только то, что может, обращая внимание на длину обоих аргументов. Другими словами, количество элементов, которые будут скопированы, равно минимальной из длин обоих срезов. Это может сэкономить немного «бюрократии». Кроме того, copy возвращает целочисленное значение – количество элементов, которые были скопированы, хотя это не всегда стоит проверки.

Функция copy так-же учитывает случаи, когда источник и приемник пересекаются (прим. пер. это как memmove() в C), это означает что функция может быть использована для перемещения элементов внутри одного среза. Ниже пример того, как с помощью функции copy вставить значение в середину среза.

Есть несколько моментов, которые вы можете заметить в этой функции. Во первых, что очевидно, она должна вернуть срез, потому что его длина изменилась. Во вторых, используется удобное сокращение. Выражение

означает тоже самое, что и

Кроме того, мы не использовали еще один трюк, мы так-же можем оставить первый элемент выражения пустым; по умолчанию это будет ноль. Таким образом

Означает просто срез самого себя, что полезно при нарезании массива. Это выражение самый короткий пусть сказать: «срез, описывающий все элементы массива»:

Но это было между делом, давайте испытаем нашу функцию Insert.

Вставка: Пример

Несколько разделов назад, мы написали функцию Extend, которая расширяла срез на один элемент. Она была неправильной, хотя бы по той причине, что в случае, когда ёмкость среза была слишком маленькой, функция могла завершиться с ошибкой (в нашем примере функция Insert подвержена такой-же проблеме). Теперь мы знаем как это исправить, поэтому давайте напишем надежную реализацию функции Extend для целочисленных срезов.

В данном случае особенно важно вернуть срез, т. к. когда мы перераспределили его, срез который у нас получился описывает совершенно другой массив. Вот небольшой кусочек для демонстрации что произойдет, если срез заполнится:

Обратите внимание на перераспределение, когда первоначальный массив размера 5 заполняется. Ёмкость и адрес нулевого элемента изменяются, когда выделяется новый массив.

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

Давайте назовем функцию Append. Для первой версии, мы можем просто вызывать Extend до тех пор, пока не закончатся аргументы функции. Прототип функции Append выглядит так:

Это говорит нам о том, что Append берет один аргумент – срез, а за ним следует от нуля до бесконечности аргументов типа int. Эти элементы будущие кусочки срез, как вы можете увидеть:

Заметьте что цикл for range выполняется для каждого элемента аргумента items, который имеет тип []int. Так-же заметьте использование пустого идентификатора _, который отбрасывает индекс, т. к. в цикле он нам не нужен.

Другой новый прием в этом примере это то, что инициализация срез производится составным литералом, который состоит из типа и элементов среза, заключенных в фигурные скобки:

Функция Append так-же интересна по другой причине. Мы можем не просто добавлять элементы, мы можем передавить в качестве аргументов функции целые срезы используя …:

Конечно, мы можем сделать Append более эффективной, путем единичного выделения, опираясь на Extend:

Обратите внимание, что здесь мы дважды используем copy, чтобы переместить срез данных в новый участок памяти и затем скопировать добавленные элементы в конец старых данных.

Попробуйте, поведение такое-же, как и прежде:

Append: Встроенная функция

Итак, мы пришли к выводу, что в Go нужно добавить встроенную функцию append. Она делает то-же самое, что и наша функция Append из примера, с одинаковой эффективностью, но работает для любого типа срезов.

Слабость Go заключается в том, что любая операция определенная на «общем-типе» должна быть предоставлена во время выполнения. Когда-нибудь это может измениться, но сейчас, дабы упростить работу со срезами, Go предоставляет встроенную общую функцию append. Она работает так-же как и наша целочисленная версия, но для любого типа среза.

Помните что поскольку заголовок среза обновляется каждый раз при вызове append, вам необходимо сохранить полученный срез после вызова. На самом деле, компилятор не позволит вам вызвать append без сохранения результата.

Вот некоторые однострочечники с выводом. Попробуйте их, изменяйте и исследуйте:

Стоит остановится и подумать о последних строчках примера и понять как дизайн срезов позволяет делать такие простые вызовы правильными.

Существует множество примеров в вики (созданной сообществом) «Slice Tricks», использующих append, copy и других путей использования срезов.

Кроме того, используя новоприобретенные знания мы можем понять что из себя представляет «нулевой» (nil) срез. Естественно, это нулевое значение заголовок среза:

Ключевым является то, что указатель тоже равен nil. Данный срез

имеет нулевую длину (и может даже нулевую ёмкость), но его указатель не nil, поэтому это все еще не нулевой срез.

Очевидно, что пустой срез может расти (предполагая что он не нулевой ёмкости), но «нулевой» (nil) срез не содержит массива, куда можно положить значения и не может вырасти, даже для того, чтобы содержать хоть один элемент.

Стоит отметить, что «нулевой» (nil) срез, по сути дела, эквивалентен срезу нулевой длины, даже если он ни на что не указывает. Он имеет нулевую длину и в него можно что-нибудь добавить с выделением. В качестве примера, посмотрите на несколько строчек выше, где копируется срез, добавляя «нулевой» (nil) срез.

Строки

Теперь кратко о строках в Go в контексте срезов.

На самом деле, строки очень просты: это срезы байт только для чтения, с небольшой дополнительной синтаксической поддержкой со стороны языка.

Так как они только для чтения, у них нет ёмкости (вы не можете увеличить их), однако в большинстве случаев вы можете обращаться с ними как срезами байт.

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

Мы можем нарезать строку для создания подстроки:

Должно быть очевидным то, что происходит за занавесом, когда мы нарезаем строку.

Так-же мы можем взять обычный срез байт и создать из него строку, путем простого преобразования:

а так-же из строки сделать срез байт:

Массив лежащий в основе строк скрыт от глаз, нет никакого способа получить к нему доступ, кроме как через строку. Это значит, что когда мы делаем эти преобразования должна быть создана копия массива. Go берет на себя эту работу, так-что не волнуйтесь об этом. После любого подобного преобразования, изменение массива лежащего в основе среза байт не влияет на соответствующую строку.

Важным следствием похожего поведения строк как срезов заключается в том, что создание подстроки происходит очень эффективно. Все что требуется, это создание двух верхушек строк. Так как строка только для чтения, то исходная строка и строка полученная в результате нарезки могут безопасно разделять один и тот-же массив.

Историческая справка: Ранние реализации строк всегда отдельно выделялись, но с тех пор в языке появились срезы, которые предоставили возможность создания более эффективной работы со строками. Некоторые бенчмарки стали показывать огромный прирост скорости.

Конечно, есть много еще того что стоит рассказать о строках, но эта тема для другой публикации.

Заключение

Понимание принципов работы срезов, помогает понять как они сделаны. Есть небольшая структура данных, заголовок среза, связанная с переменной типа срез и эта заголовок описывает часть отдельно выделенного массива. Когда мы создаем срез, то заголовок копируется, но массив всегда разделяется.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *