Eliminar elemento de una matriz regular

135

Tengo una gran variedad de objetos Foo. ¿Cómo elimino el segundo elemento de la matriz?

Necesito algo similar RemoveAt()pero para una matriz regular.

leora
fuente
1
Uso System.Collections.ObjectModel.Collection<Foo>.
abatishchev
1
Para mi juego, elegí una estructura de datos "nulo en el índice". Básicamente, la matriz interna (buffer) es de tamaño estático, y en lugar de eliminar el índice y cambiar el tamaño de la matriz, simplemente hago que el índice sea nulo. Cuando necesito agregar un elemento, solo encuentro el primer índice no nulo y lo coloco allí. Funciona bastante bien, pero obviamente no para todo.
Krythic

Respuestas:

202

Si no quieres usar la Lista:

var foos = new List<Foo>(array);
foos.RemoveAt(index);
return foos.ToArray();

Podrías probar este método de extensión que no he probado:

public static T[] RemoveAt<T>(this T[] source, int index)
{
    T[] dest = new T[source.Length - 1];
    if( index > 0 )
        Array.Copy(source, 0, dest, 0, index);

    if( index < source.Length - 1 )
        Array.Copy(source, index + 1, dest, index, source.Length - index - 1);

    return dest;
}

Y úsalo como:

Foo[] bar = GetFoos();
bar = bar.RemoveAt(2);
Andrew Kennan
fuente
8
El primer ejemplo dado en esta respuesta es mucho menos eficiente que el segundo. Requiere dos copias de matriz y un cambio de todo después del índice en lugar de una copia de matriz selectiva.
Martin Brown
2
+1, por supuesto, pero también podemos usar la lista OR O List <Foo> list = new List <Foll> (GetFoos ()); list.Remove (my_foo); list.RemoveAt (2); donde GetFoos () devolverá la matriz de Foos !!!!
shahjapan
2
La primera línea dentro del método debería decir 'source.Length' en lugar de 'array.Length'.
Nelson
1
Además, tenga en cuenta que cualquier variable que almacene una referencia a la matriz original continuará conteniendo los datos originales y que cualquier comparación de igualdad de referencia entre la matriz en la fuente y la matriz de salida devolverá un valor negativo.
bkqc
1
@MartinBrown En realidad, la conversión de una lista a \ from y array es mucho más lenta que una copia de array (que puede copiar los datos a la velocidad máxima permitida por la CPU con solo unas pocas instrucciones ASM). Además, cambiar una lista es muy rápido porque solo se trata de intercambiar algunos punteros y eliminar los datos del nodo (que en este caso son solo 8 bytes [más otros 16 para los punteros head / tail]).
krowe2
66

La naturaleza de las matrices es que su longitud es inmutable. No puede agregar ni eliminar ninguno de los elementos de la matriz.

Tendrá que crear una nueva matriz que sea un elemento más corta y copiar los elementos antiguos a la nueva matriz, excluyendo el elemento que desea eliminar.

Por lo tanto, probablemente sea mejor usar una Lista en lugar de una matriz.

Sebastian Dietz
fuente
44
Convierta el conjunto en una listaList<mydatatype> array = new List<mydatatype>(arrayofmydatatype)
Immortal Blue
1
@ImmortalBlue o simplemente var myList = myArray.ToList();usando el Enumerable.ToList()método del System.Linqespacio de nombres.
Dyndrilliac
58

Yo uso este método para eliminar un elemento de una matriz de objetos. En mi situación, mis matrices son pequeñas en longitud. Entonces, si tiene matrices grandes, es posible que necesite otra solución.

private int[] RemoveIndices(int[] IndicesArray, int RemoveAt)
{
    int[] newIndicesArray = new int[IndicesArray.Length - 1];

    int i = 0;
    int j = 0;
    while (i < IndicesArray.Length)
    {
        if (i != RemoveAt)
        {
            newIndicesArray[j] = IndicesArray[i];
            j++;
        }

        i++;
    }

    return newIndicesArray;
}
EdHellyer
fuente
77
Personalmente, me gusta esta respuesta mejor que la respuesta aceptada. Debe ser igual de eficiente y es mucho más fácil de leer. Puedo mirarlo y saber que es correcto. Tendría que probar el otro para asegurarme de que esas copias se escribieron correctamente.
Oillio
1
Realmente es una pena que esta respuesta sea tan baja, cuando es mucho mejor que las dos anteriores.
Sepulcritud
¡Aaarhg, esa es la respuesta que estaba buscando! Este es el mejor método sin listas.
Jordi Huertas
47

Solución de una línea LINQ:

myArray = myArray.Where((source, index) => index != 1).ToArray();

El 1en ese ejemplo es el índice del elemento a eliminar: en este ejemplo, según la pregunta original, el segundo elemento ( 1siendo el segundo elemento en la indexación de matriz basada en C # cero).

Un ejemplo más completo:

string[] myArray = { "a", "b", "c", "d", "e" };
int indexToRemove = 1;
myArray = myArray.Where((source, index) => index != indexToRemove).ToArray();

Después de ejecutar ese fragmento, el valor de myArrayserá { "a", "c", "d", "e" }.

Jon Schneider
fuente
1
Para áreas que requieren acceso frecuente / de alto rendimiento, no se recomienda LINQ.
Krythic
3
@Krythic Ese es un comentario justo. Ejecutado miles de veces en un ciclo cerrado, el rendimiento de esta solución no es tan bueno como algunas de las otras soluciones altamente votadas en esta página: dotnetfiddle.net/z9Xkpn
Jon Schneider
9

Esta es una forma de eliminar un elemento de matriz, a partir de .Net 3.5, sin copiar a otra matriz, utilizando la misma instancia de matriz con Array.Resize<T>:

public static void RemoveAt<T>(ref T[] arr, int index)
{
    for (int a = index; a < arr.Length - 1; a++)
    {
        // moving elements downwards, to fill the gap at [index]
        arr[a] = arr[a + 1];
    }
    // finally, let's decrement Array's size by one
    Array.Resize(ref arr, arr.Length - 1);
}
infografnet
fuente
2
"sin copiar a otra matriz" - por la documentación vinculada, Array.Resize realidad hace asignar una nueva matriz detrás de las escenas, y copia los elementos de la matriz de edad a la nueva. Aún así, me gusta la concisión de esta solución.
Jon Schneider
Muy bonito y claro si estás seguro de que es una matriz relativamente pequeña.
Darren
1
Continuando el comentario de @ JonSchneider, no es "la misma instancia de matriz". Es por eso que debes usar refcuando llamas al Resizemétodo. La longitud de una instancia de matriz es fija e inmutable.
Jeppe Stig Nielsen
2
Si el orden de los elementos no es importante, en lugar de mover todos los elementos hacia abajo, puede intercambiar el elemento en el índice con el último elemento y luego cambiar el tamaño: arr [index] = arr [arr.Length - 1]; Array.Resize (ref arr, arr.Length - 1);
Bartel
5

Aquí hay una versión anterior que tengo que funciona en la versión 1.0 del marco .NET y no necesita tipos genéricos.

public static Array RemoveAt(Array source, int index)
{
    if (source == null)
        throw new ArgumentNullException("source");

    if (0 > index || index >= source.Length)
        throw new ArgumentOutOfRangeException("index", index, "index is outside the bounds of source array");

    Array dest = Array.CreateInstance(source.GetType().GetElementType(), source.Length - 1);
    Array.Copy(source, 0, dest, 0, index);
    Array.Copy(source, index + 1, dest, index, source.Length - index - 1);

    return dest;
}

Esto se usa así:

class Program
{
    static void Main(string[] args)
    {
        string[] x = new string[20];
        for (int i = 0; i < x.Length; i++)
            x[i] = (i+1).ToString();

        string[] y = (string[])MyArrayFunctions.RemoveAt(x, 3);

        for (int i = 0; i < y.Length; i++)
            Console.WriteLine(y[i]);
    }
}
Martin Brown
fuente
3

No es exactamente la forma de hacerlo, pero si la situación es trivial y usted valora su tiempo, puede intentar esto para los tipos anulables.

Foos[index] = null

y luego verifique si hay entradas nulas en su lógica.

nawfal
fuente
Así es como lo hice para mi juego. Vaya con buffers anulables para áreas que se cambian con mucha frecuencia.
Krythic
2

Como de costumbre, llego tarde a la fiesta ...

Me gustaría agregar otra opción a la lista de buenas soluciones ya presentes. =)
Vería esto como una buena oportunidad para Extensiones.

Referencia: http://msdn.microsoft.com/en-us/library/bb311042.aspx

Entonces, definimos alguna clase estática y en ella, nuestro Método.
Después de eso, podemos usar nuestro método extendido willy-nilly. =)

using System;

namespace FunctionTesting {

    // The class doesn't matter, as long as it's static
    public static class SomeRandomClassWhoseNameDoesntMatter {

        // Here's the actual method that extends arrays
        public static T[] RemoveAt<T>( this T[] oArray, int idx ) {
            T[] nArray = new T[oArray.Length - 1];
            for( int i = 0; i < nArray.Length; ++i ) {
                nArray[i] = ( i < idx ) ? oArray[i] : oArray[i + 1];
            }
            return nArray;
        }
    }

    // Sample usage...
    class Program {
        static void Main( string[] args ) {
            string[] myStrArray = { "Zero", "One", "Two", "Three" };
            Console.WriteLine( String.Join( " ", myStrArray ) );
            myStrArray = myStrArray.RemoveAt( 2 );
            Console.WriteLine( String.Join( " ", myStrArray ) );
            /* Output
             * "Zero One Two Three"
             * "Zero One Three"
             */

            int[] myIntArray = { 0, 1, 2, 3 };
            Console.WriteLine( String.Join( " ", myIntArray ) );
            myIntArray = myIntArray.RemoveAt( 2 );
            Console.WriteLine( String.Join( " ", myIntArray ) );
            /* Output
             * "0 1 2 3"
             * "0 1 3"
             */
        }
    }
}
Duncan
fuente
2

Pruebe el siguiente código:

myArray = myArray.Where(s => (myArray.IndexOf(s) != indexValue)).ToArray();

o

myArray = myArray.Where(s => (s != "not_this")).ToArray();
NovatechGuy
fuente
1

Así es como lo hice ...

    public static ElementDefinitionImpl[] RemoveElementDefAt(
        ElementDefinition[] oldList,
        int removeIndex
    )
    {
        ElementDefinitionImpl[] newElementDefList = new ElementDefinitionImpl[ oldList.Length - 1 ];

        int offset = 0;
        for ( int index = 0; index < oldList.Length; index++ )
        {
            ElementDefinitionImpl elementDef = oldList[ index ] as ElementDefinitionImpl;
            if ( index == removeIndex )
            {
                //  This is the one we want to remove, so we won't copy it.  But 
                //  every subsequent elementDef will by shifted down by one.
                offset = -1;
            }
            else
            {
                newElementDefList[ index + offset ] = elementDef;
            }
        }
        return newElementDefList;
    }
Paul Mitchell
fuente
1

En una matriz normal, debe mezclar todas las entradas de la matriz por encima de 2 y luego cambiar su tamaño utilizando el método Cambiar tamaño. Puede que sea mejor usar una ArrayList.

gkrogers
fuente
1
    private int[] removeFromArray(int[] array, int id)
    {
        int difference = 0, currentValue=0;
        //get new Array length
        for (int i=0; i<array.Length; i++)
        {
            if (array[i]==id)
            {
                difference += 1;
            }
        }
        //create new array
        int[] newArray = new int[array.Length-difference];
        for (int i = 0; i < array.Length; i++ )
        {
            if (array[i] != id)
            {
                newArray[currentValue] = array[i];
                currentValue += 1;
            }
        }

        return newArray;
    }
usuario2884232
fuente
0

Aquí hay una pequeña colección de métodos auxiliares que produje en base a algunas de las respuestas existentes. Utiliza tanto extensiones como métodos estáticos con parámetros de referencia para la máxima idealidad:

public static class Arr
{
    public static int IndexOf<TElement>(this TElement[] Source, TElement Element)
    {
        for (var i = 0; i < Source.Length; i++)
        {
            if (Source[i].Equals(Element))
                return i;
        }

        return -1;
    }

    public static TElement[] Add<TElement>(ref TElement[] Source, params TElement[] Elements)
    {
        var OldLength = Source.Length;
        Array.Resize(ref Source, OldLength + Elements.Length);

        for (int j = 0, Count = Elements.Length; j < Count; j++)
            Source[OldLength + j] = Elements[j];

        return Source;
    }

    public static TElement[] New<TElement>(params TElement[] Elements)
    {
        return Elements ?? new TElement[0];
    }

    public static void Remove<TElement>(ref TElement[] Source, params TElement[] Elements)
    {
        foreach (var i in Elements)
            RemoveAt(ref Source, Source.IndexOf(i));
    }

    public static void RemoveAt<TElement>(ref TElement[] Source, int Index)
    {
        var Result = new TElement[Source.Length - 1];

        if (Index > 0)
            Array.Copy(Source, 0, Result, 0, Index);

        if (Index < Source.Length - 1)
            Array.Copy(Source, Index + 1, Result, Index, Source.Length - Index - 1);

        Source = Result;
    }
}

En cuanto al rendimiento, es decente, pero probablemente podría mejorarse. Removese basa IndexOfy se crea una nueva matriz para cada elemento que desea eliminar llamando RemoveAt.

IndexOfes el único método de extensión ya que no necesita devolver la matriz original. Newacepta múltiples elementos de algún tipo para producir una nueva matriz de dicho tipo. Todos los demás métodos deben aceptar la matriz original como referencia, por lo que no es necesario asignar el resultado después, ya que eso ya ocurre internamente.

Hubiera definido un Mergemétodo para fusionar dos matrices; sin embargo, eso ya se puede lograr con el Addmétodo pasando una matriz real frente a múltiples elementos individuales. Por lo tanto, Addse puede usar de las dos formas siguientes para unir dos conjuntos de elementos:

Arr.Add<string>(ref myArray, "A", "B", "C");

O

Arr.Add<string>(ref myArray, anotherArray);
James M
fuente
-1

Sé que este artículo tiene diez años y, por lo tanto, probablemente esté muerto, pero esto es lo que intentaría hacer:

Use el método IEnumerable.Skip (), que se encuentra en System.Linq . Saltará el elemento seleccionado de la matriz y devolverá otra copia de la matriz que solo contiene todo excepto el objeto seleccionado. Luego repita eso para cada elemento que desee eliminar y luego guárdelo en una variable.

Por ejemplo, si tenemos una matriz llamada "Muestra" (de tipo int []) con 5 números. Queremos eliminar el segundo, así que intenta "Sample.Skip (2);" debería devolver la misma matriz, excepto sin el segundo número.

commandertuna
fuente
¿Este método no pasa por alto un número específico de elementos en una secuencia y luego devuelve los elementos restantes ? En su ejemplo, "omitirá" los dos primeros elementos de la lista genérica y no solo el segundo.
xnr_z
-4

Primer paso
Necesitas convertir la matriz en una lista, puedes escribir un método de extensión como este

// Convert An array of string  to a list of string
public static List<string> ConnvertArrayToList(this string [] array) {

    // DECLARE a list of string and add all element of the array into it

    List<string> myList = new List<string>();
    foreach( string s in array){
        myList.Add(s);
    }
    return myList;
} 

Segundo paso
Escriba un método de extensión para volver a convertir la lista en una matriz

// convert a list of string to an array 
public static string[] ConvertListToArray(this List<string> list) {

    string[] array = new string[list.Capacity];
    array = list.Select(i => i.ToString()).ToArray();
    return array;
}

Últimos pasos
Escriba su método final, pero recuerde eliminar el elemento en el índice antes de volver a convertirlo en una matriz como se muestra en el código

public static string[] removeAt(string[] array, int index) {

    List<string> myList = array.ConnvertArrayToList();
    myList.RemoveAt(index);
    return myList.ConvertListToArray();
} 

ejemplos de códigos se pueden encontrar en mi blog , sigue el seguimiento.

Bamara Coulibaly
fuente
13
Esto es ligeramente loco teniendo en cuenta la existencia de .ToArray()y un List<T>constructor que tiene una secuencia existente ...
user7116