Как удалить элемент по определенному индексу в массиве?

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

Если нет, кто-нибудь знает, как реализовать такой метод?

Вы хотите просто удалить элемент или удалить его и переместить все вниз по индексу?
Я бы хотел, чтобы все было по индексу, поэтому, если я удалю [2], тогда [3] станет [2]

Ответы (7)

Используйте deleteоператор для удаления элемента:

delete array[index];

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

contract test{
    uint[] array = [1,2,3,4,5];
    function remove(uint index)  returns(uint[]) {
        if (index >= array.length) return;

        for (uint i = index; i<array.length-1; i++){
            array[i] = array[i+1];
        }
        delete array[array.length-1];
        array.length--;
        return array;
    }
}

Если вас не волнует порядок, вы также можете просто скопировать последний элемент в пустое место, а затем удалить последний элемент.

Спасибо! У меня просто есть проблема с функцией, которую вы дали при попытке сделать ее более "общей": function remove(uint[] array,uint index) returns(uint[]) {дает мне Error: Expression has to be an lvalue. array.length--;Кроме того, этот метод может быть адаптирован для работы с массивом всех типов (структуры и т.д.)?
Я не уверен в уменьшении длины массивов в памяти, а не в хранилище. Я думаю, вы можете скопировать все это в новый массив или просто удалить эту строку и оставить нули в конце. В любом случае, вы не сэкономите газ, сделав массив памяти короче. Этот метод должен работать с любым типом, кроме сопоставлений, поскольку deleteв сопоставлениях он не имеет особого смысла.
если массив очень большой, например 1000 объектов, не должен ли он потреблять очень большое количество газа?
@TjadenHess можно ли удалить элемент массива из другого контракта, если массив является общедоступным? Я знаю, что с этим есть проблемы, но я хочу реализовать это. Заранее спасибо.
Нет, вы не можете сделать это из коробки. Другой контракт должен предоставить некоторую функцию для удаления элементов массива.
Как я могу удалить весь массив?
Эта функция может остановить выполнение контракта, если массив очень длинный.
Уменьшение длины массива автоматически очистит слоты хранения, занятые выходящими за границы элементами. Так что линия delete array[array.length-1];лишняя. Кроме того, он добавляет 5000 газа к транзакции, так как возврат газа применяется только в случае, когда хранилище сбрасывается с ненулевого значения на нулевое. Если он установлен от нуля до нуля (добавлено компилятором), он стоит 5000 газа.
Связывание этого вопроса с ethereum.stackexchange.com/questions/39303/…
Интересно, может быть более эффективно использовать сборку как-то для смещения указателей, чтобы избежать пробела, или объединить новый массив с левой и правой сторон пробела?
Что ж, правильный способ сделать это, вероятно, будет использовать связанный список вместо массива.
Лучший способ, который я протестировал, это скопировать последний элемент в удаленное место, вызвать удаление для последнего индекса, а затем уменьшить длину массива. Это постоянный объем работы, а не линейный. Если вам важен порядок, у вас должно быть дополнительное сопоставление этого элемента со следующим, которое вы поддерживаете при добавлении и удалении.
Есть ли какое-либо преимущество в удалении элемента из массива вместо присвоения какого -либо нулевого значения, когда я не перестраиваю массив?
массив.длина--; выдает ошибку только для чтения в 0.7.0
Начиная с версии 0.6.0, pop() — единственный способ уменьшить размер массива. « Больше невозможно изменить размер массивов хранения, присвоив новое значение их длине » - docs.soliditylang.org/en/v0.8.11/…

Эта постоянная операция работает без сохранения порядка:

uint[] internal array;

// Move the last element to the deleted spot.
// Remove the last element.
function _burn(uint index) internal {
  require(index < array.length);
  array[index] = array[array.length-1];
  array.pop();
}

Чтобы сохранить порядок при отзыве, не неся затраты газа на сдвиг значений справа от промежутка, вам потребуется дополнительное сопоставление между индексом каждого элемента с индексом его преемника, которое вам необходимо поддерживать во время вставки и удаления:mapping(uint => uint) private indexAfter;

Лучший ответ!!!!
Однозначно лучший
Начиная с Solidity 0.6.0 это не должно работать ethereum.stackexchange.com/questions/80743/…
Хорошее решение. Работает на 0.8.0

Небольшая оптимизация ответа Тьядена Хесса:

contract Test {
    uint[] array = [1,2,3,4,5];
    function remove(uint index)  returns(uint[]) {
        if (index >= array.length) return;

        for (uint i = index; i<array.length-1; i++){
            array[i] = array[i+1];
        }
        array.length--;
        return array;
    }
}

Я удалил строку delete array[array.length-1];раньше array.length--;. Это удешевляет функцию на 5000 газа. Компилятор автоматически очищает незанятые слоты при уменьшении длины массива. Двойной сброс хранилища добавляет 5000 газа.

Я нашел этот ответ только для работы с массивами хранения, можете ли вы подтвердить? Массив.длина--; строка вызовет исключение. При изменении его так, чтобы больше не было ошибки, он не удалит последний элемент массива.
@Nico, да, array.length - работает только с массивами хранения, для массивов памяти длина остается постоянной после ее назначения. если вам нужен массив памяти разной длины, вам нужно объявить новый массив памяти.
Какова цель оператора return в конце функции?
array.pop() вместо array.length -- для плотности > 0,6 даже для массивов хранения.

Большинство предыдущих ответов напрямую изменяют длину массива, чтобы уменьшить его длину.

Начиная с Solidity 0.6.0 это больше невозможно

Доступ членов к массивам длины теперь всегда доступен только для чтения, даже для массивов хранения. Больше нельзя изменять размер массивов хранения, присваивая новое значение их длине. Вместо этого используйте push(), push(value) или pop() или назначьте полный массив, который, конечно, перезапишет существующее содержимое. Причина этого заключается в предотвращении коллизий хранения из-за гигантских массивов хранения.

https://docs.soliditylang.org/en/v0.6.2/060-breaking-changes.html

Вы можете исправить ответ medvedev1088 с помощью:

contract Test {
    uint[] array = [1,2,3,4,5];
    function remove(uint index)  returns(uint[]) {
        if (index >= array.length) return;

        for (uint i = index; i<array.length-1; i++){
            array[i] = array[i+1];
        }
        array.pop();
        return array;
    }
}

Обратите внимание: array.pop();вместоarray.length--;

pragma solidity ^0.4.11;
contract TestArray {
    uint[] public original;
    uint[] public newOr;
    event Log(uint n, uint a, uint b, uint c);

    function TestArray(){
        original.push(1);
        original.push(2);
        original.push(3);
        original.push(4);

    }

    function test(){
        newOr = remove(original, 1);
        Log(newOr.length, newOr[0], newOr[1], newOr[2]);
    }
    function remove(uint[] array, uint index) internal returns(uint[] value) {
        if (index >= array.length) return;

        uint[] memory arrayNew = new uint[](array.length-1);
        for (uint i = 0; i<arrayNew.length; i++){
            if(i != index && i<index){
                arrayNew[i] = array[i];
            } else {
                arrayNew[i] = array[i+1];
            }
        }
        delete array;
        return arrayNew;
    }

}
Как я могу удалить весь массив?
Вам нужно удалить все элементы один за другим. Сам массив нельзя удалить, так как он является переменной хранения и вечно живет в пространстве контракта.

delete a присваивает начальное значение типа a. Т.е. для целых чисел это эквивалентно a = 0, но его также можно использовать для массивов, где он присваивает динамический массив нулевой длины или статический массив той же длины со сбросом всех элементов. Для структур он назначает структуру со всеми сброшенными элементами.

> Я реализовал это, может быть полезно понять этот простой пример

**

И если мы удалим элемент с помощью индекса, он не оставит пробел.

**

http://solidity.readthedocs.io/en/v0.4.21/types.html

contract UserRecord {
    constructor() public { owner = msg.sender; }

    address owner;

    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }

    struct User {
        bytes32 userEmail;
        uint index;
    }

    mapping (bytes32 => User) private users;

    bytes32[] private usersRecords;
    event LogNewUser(bytes32 indexed userEmail, uint index);

    function setUseremail(bytes32 _userEmail) public onlyOwner returns(bool success){
        users[_userEmail].userEmail = _userEmail;
        users[_userEmail].index = usersRecords.push(_userEmail) -1;

        emit LogNewUser(
        _userEmail,
        users[_userEmail].index
        );
        return true;
    }

//this will delete the user at particular index and gap will be not there

    function deleteUser(bytes32 _userEmail) public onlyOwner returns(uint index){
        require(!isUser(_userEmail)); 
        uint toDelete = users[_userEmail].index;
        bytes32 lastIndex = usersRecords[usersRecords.length-1];
        usersRecords[toDelete] = lastIndex;
        users[lastIndex].index = toDelete; 
        usersRecords.length--;
        return toDelete;   
    }    
}

Решение, которое потребляет немного больше газа по сравнению с другими.

// Preload any custom data through other functions
address[] customArray;

function removeIndex(uint256 index) external {
    address[] storage _array = customArray;
    _array.push(_array[index]);
    for (uint i=index; i<_array.length-1; i++){
        _array[i] = _array[i+1];
    }
    _array.pop();
    _array.pop();
    customArray = _array;
}
Зачем лишний толчок? Это кажется бесполезным.