67

Anyone know how to delete an element in a array? Is there any built in method to do that?

If not, does anyone know of how to implement such a method?

J-B
  • 8,941
  • 16
  • 46
  • 77
user697
  • 2,176
  • 3
  • 15
  • 19

9 Answers9

86

Use the delete operator to delete the element:

delete array[index];

If you don't want to leave a gap, you need to move each element manually:

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;
    }
}

If you don't care about the order, you can also just copy the last element into the empty spot, then delete the last element.

Tjaden Hess
  • 37,046
  • 10
  • 91
  • 118
  • 1
    Thanks! I just have an issue with the function you gave when trying to make it more " general :

    function remove(uint[] array,uint index) returns(uint[]) {

    gives me Error: Expression has to be an lvalue. array.length--;

    Also , does that method can be adapted to work on array of all types ( struct , etc ) ?

    – user697 Feb 21 '16 at 02:35
  • I'm not sure about decreasing the length of arrays in memory, as opposed to storage. You can copy it all over to a new array, I guess, or just remove that line and leave the trailing zeros. You won't save any gas by making a memory array shorter, anyway. This method should work on any type, except mappings, since delete doesn't really make sense in mappings. – Tjaden Hess Feb 21 '16 at 02:41
  • if the array is very large for example 1000 objects, should not it consume very large amount of gas? – alper Oct 21 '16 at 13:47
  • @TjadenHess is it possible to delete a array member from another contract if the array is public? I know there are problems with that but I want to implement that. Thanks in advance. – 11t Mar 29 '17 at 07:37
  • No, you can't do that out of the box. The other contract needs to provide some function for deleting array elements – Tjaden Hess Mar 29 '17 at 14:42
  • How could I delete the complete array? – Mago Nicolas Palacios Sep 22 '17 at 00:49
  • This function can leave the contract stalled if the array is very long. – Fernando Garcia Nov 02 '17 at 08:16
  • 9
    Decreasing array length will automatically clean up the storage slots occupied by the out-of-bounds elements. So the line delete array[array.length-1]; is redundant. Moreover it adds 5000 gas to the transaction since gas refund applies only in the case when storage is reset from non-zero to zero value. If it's set from zero to zero (added by compiler) it costs 5000 gas. – medvedev1088 Feb 11 '18 at 20:02
  • Linking this question https://ethereum.stackexchange.com/questions/39303/resetting-storage-slot-increases-gas-usage-although-should-decrease-it – medvedev1088 Feb 11 '18 at 20:18
  • I wonder if it might be more efficient to use assembly somehow to shift pointers to avoid gap or concat a new array from the left and right sides of the gap – ABCD.ca Jun 16 '18 at 17:33
  • Well, the correct way to do this would probably be to use a linked list instead of an array – Tjaden Hess Jun 17 '18 at 08:08
  • 4
    The best way I tested that works is to copy the last element to the deleted spot, call delete on the last index, then decrement the array length. This is a constant amount of work, not linear. If you care about the order, you should have an additional mapping from this element to next, that you maintain during addition and deletion. – Amin Ariana Sep 21 '18 at 20:03
  • Is there any advantage to delete an item from an array, rather than assigning some nullish value, when I don't re-arrange the array? – Qwerty Jan 05 '22 at 16:12
  • array.length--; throws read-only error in 0.7.0 – user938363 Jan 11 '22 at 17:50
  • 1
    From 0.6.0, pop() is the only way to reduce size of array. "It is no longer possible to resize storage arrays by assigning a new value to their length" - https://docs.soliditylang.org/en/v0.8.11/060-breaking-changes.html?highlight=Pop#explicitness-requirements – Udo E. Feb 17 '22 at 21:31
37

This constant operation works without preserving order:

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(); }

To preserve order on recall without incurring the gas cost of shifting right-of-gap values, you'll need an additional mapping between each element's index to its successor's index that you need to maintain during insertion and deletion: mapping(uint => uint) private indexAfter;

Neo
  • 158
  • 1
  • 2
  • 13
Amin Ariana
  • 471
  • 4
  • 4
19

Small optimisation to Tjaden Hess' answer:

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;
    }
}

I removed the line delete array[array.length-1]; before array.length--;. This makes the function cheaper by 5000 gas. The compiler will automatically clean up unoccupied slots when array length is decreased. Double storage resetting adds 5000 gas.

medvedev1088
  • 10,996
  • 5
  • 35
  • 63
  • 3
    I found this answer only to work on storage arrays, can you confirm? The array.length--; line will throw an exception. When changing it so that there is no error anymore it will not remove the last element of the array. – Nico May 18 '18 at 08:40
  • 1
    @Nico, yup, array.length-- only works on storage arrays, for memory arrays, the length is constant after it's assigned. if you want a memory array of different length, you need to declare a new memory array. – hellopeach Jul 31 '19 at 12:10
  • What is the purpose of the return statement at the end of the function? – Derrick Miller Dec 09 '19 at 02:40
  • 4
    array.pop() instead of array.length-- for solidity > 0.6 even for storage arrays. – Zatara7 Aug 20 '20 at 19:16
15

Most of the previous answers directly modify the array length to reduce its length.

Since Solidity 0.6.0 this is no longer possible

Member-access to length of arrays is now always read-only, even for storage arrays. It is no longer possible to resize storage arrays assigning a new value to their length. Use push(), push(value) or pop() instead, or assign a full array, which will of course overwrite existing content. The reason behind this is to prevent storage collisions by gigantic storage arrays.

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

You can fix medvedev1088's answer with:

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&lt;array.length-1; i++){
        array[i] = array[i+1];
    }
    array.pop();
    return array;
}

}

Notice: array.pop(); instead of array.length--;

Diego Ferri
  • 926
  • 8
  • 12
2
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;
    }

}
rstormsf
  • 4,337
  • 2
  • 25
  • 42
1

Instructions: You can put your last index object value into the deleted value index (the index whose value you want to delete), then delete the last object or value in the array.

It's Working Like that:

uint[] Element = [10,20,30,40,50];

function _removeElementByIndex(Element[] storage array, uint256 index) private { array[index] = array[array.length - 1]; array.pop(); }

0

A solution that consumes a bit more gas compared to others.

// 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; }

0

Solidity 0.8 compatible version based on Tjaden Hess answer:

pragma solidity >0.8.0;

contract ArrayDeleteItemTest { uint[] public array = [1,2,3,4,5];

function remove(uint index) public returns (uint[] memory) {
    require(index &lt; array.length, &quot;index out of bounds&quot;);

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

    array.pop();

    return array;
}

function printFullArray() public view returns (uint[] memory) {
    return array;
}

}

You can copy it straight to Remix and test:

Before remove(3):

After:

Daniel
  • 359
  • 4
  • 16
0

delete a assigns the initial value for the type to a. I.e. for integers it is equivalent to a = 0, but it can also be used on arrays, where it assigns a dynamic array of length zero or a static array of the same length with all elements reset. For structs, it assigns a struct with all members reset.

> I have implemented it, may be helpful to understand by this simple example

**

And if we remove the element using index it will not leave the gap.

**

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;   
    }    
}