¿Cómo eliminar un elemento en un cierto índice en una matriz?

¿Alguien sabe cómo eliminar un elemento en una matriz? ¿Hay algún método incorporado para hacer eso?

Si no, ¿alguien sabe cómo implementar dicho método?

¿Desea simplemente eliminar el elemento o eliminarlo y mover todo hacia abajo en un índice?
Me gustaría que todo estuviera en un índice, así que si elimino un [2], entonces un [3] se convierte en un [2]

Respuestas (7)

Utilice el deleteoperador para eliminar el elemento:

delete array[index];

Si no desea dejar un espacio, debe mover cada elemento manualmente:

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

Si no le importa el orden, también puede simplemente copiar el último elemento en el lugar vacío y luego eliminar el último elemento.

¡Gracias! Solo tengo un problema con la función que diste al tratar de hacerla más "general: function remove(uint[] array,uint index) returns(uint[]) {me da". Error: Expression has to be an lvalue. array.length--;Además, ¿ese método se puede adaptar para trabajar en matrices de todo tipo (struct, etc.)?
No estoy seguro de disminuir la longitud de las matrices en la memoria, a diferencia del almacenamiento. Puede copiarlo todo en una nueva matriz, supongo, o simplemente eliminar esa línea y dejar los ceros finales. De todos modos, no ahorrará gasolina al hacer una matriz de memoria más corta. Este método debería funcionar en cualquier tipo, excepto en las asignaciones, ya deleteque en realidad no tiene sentido en las asignaciones.
si la matriz es muy grande, por ejemplo, 1000 objetos, ¿no debería consumir una gran cantidad de gas?
@TjadenHess, ¿es posible eliminar un miembro de la matriz de otro contrato si la matriz es pública? Sé que hay problemas con eso, pero quiero implementar eso. Gracias por adelantado.
No, no puedes hacer eso fuera de la caja. El otro contrato debe proporcionar alguna función para eliminar elementos de la matriz.
¿Cómo podría eliminar la matriz completa?
Esta función puede dejar el contrato estancado si la matriz es muy larga.
La disminución de la longitud de la matriz limpiará automáticamente las ranuras de almacenamiento ocupadas por los elementos fuera de los límites. Así que la línea delete array[array.length-1];es redundante. Además, agrega 5000 de gas a la transacción, ya que el reembolso de gas se aplica solo en el caso de que el almacenamiento se restablezca de un valor distinto de cero a cero. Si está configurado de cero a cero (agregado por el compilador), cuesta 5000 de gasolina.
Me pregunto si podría ser más eficiente usar el ensamblaje de alguna manera para cambiar los punteros para evitar espacios o concatenar una nueva matriz desde los lados izquierdo y derecho del espacio.
Bueno, la forma correcta de hacer esto probablemente sería usar una lista enlazada en lugar de una matriz
La mejor manera que probé que funciona es copiar el último elemento en el lugar eliminado, llamar a eliminar en el último índice y luego disminuir la longitud de la matriz. Esta es una cantidad constante de trabajo, no lineal. Si le importa el orden, debe tener una asignación adicional de este elemento al siguiente, que mantiene durante la adición y eliminación.
¿Hay alguna ventaja en eliminar un elemento de una matriz, en lugar de asignar un valor nulo , cuando no reorganizo la matriz?
matriz.longitud--; arroja un error de solo lectura en 0.7.0
Desde 0.6.0, pop() es la única forma de reducir el tamaño de la matriz. " Ya no es posible cambiar el tamaño de las matrices de almacenamiento asignando un nuevo valor a su longitud " - docs.soliditylang.org/en/v0.8.11/…

Esta operación constante funciona sin conservar el orden:

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

Para conservar el orden en la recuperación sin incurrir en el costo de gas de cambiar los valores de derecho de brecha, necesitará una asignación adicional entre el índice de cada elemento y el índice de su sucesor que debe mantener durante la inserción y eliminación:mapping(uint => uint) private indexAfter;

¡¡¡¡La mejor respuesta!!!!
Definitivamente el mejor
Desde solidity 0.6.0 no debería funcionar ethereum.stackexchange.com/questions/80743/…
Buena solución. Funciona en 0.8.0

Pequeña optimización de la respuesta de Tjaden Hess:

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

Quité la línea delete array[array.length-1];antes array.length--;. Esto abarata la función por 5000 de gas. El compilador limpiará automáticamente las ranuras desocupadas cuando se reduzca la longitud de la matriz. El restablecimiento de almacenamiento doble agrega 5000 de gas.

Encontré esta respuesta solo para trabajar en matrices de almacenamiento, ¿puede confirmar? La matriz.longitud--; la línea lanzará una excepción. Al cambiarlo para que ya no haya errores, no eliminará el último elemento de la matriz.
@Nico, sí, array.length: solo funciona en matrices de almacenamiento, para matrices de memoria, la longitud es constante después de que se asigna. si desea una matriz de memoria de diferente longitud, debe declarar una nueva matriz de memoria.
¿Cuál es el propósito de la declaración de retorno al final de la función?
array.pop() en lugar de array.length-- para solidez > 0.6 incluso para matrices de almacenamiento.

La mayoría de las respuestas anteriores modifican directamente la longitud de la matriz para reducir su longitud.

Desde Solidity 0.6.0 esto ya no es posible

El acceso de los miembros a la longitud de los arreglos ahora es siempre de solo lectura, incluso para los arreglos de almacenamiento. Ya no es posible cambiar el tamaño de los arreglos de almacenamiento asignando un nuevo valor a su longitud. Use push(), push(value) o pop() en su lugar, o asigne una matriz completa, que por supuesto sobrescribirá el contenido existente. La razón detrás de esto es evitar colisiones de almacenamiento por matrices de almacenamiento gigantes.

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

Puede corregir la respuesta de medvedev1088 con:

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

Aviso: array.pop();en lugar dearray.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;
    }

}
¿Cómo podría eliminar la matriz completa?
Debe eliminar todos los elementos uno por uno. La matriz en sí no se puede eliminar, ya que es una variable de almacenamiento y vive para siempre en el espacio del contrato.

delete a asigna el valor inicial del tipo a a. Es decir, para números enteros es equivalente a a = 0, pero también se puede usar en arreglos, donde asigna un arreglo dinámico de longitud cero o un arreglo estático de la misma longitud con todos los elementos reiniciados. Para estructuras, asigna una estructura con todos los miembros reiniciados.

> Lo he implementado, puede ser útil entenderlo con este simple ejemplo

**

Y si eliminamos el elemento usando el índice, no dejará el espacio.

**

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

Una solución que consume un poco más de gas en comparación con otras.

// 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;
}
¿Por qué el empujón extra? Parece inútil.