Rimuovi l'elemento di un array normale


135

Ho una serie di oggetti Foo. Come rimuovo il secondo elemento dell'array?

Ho bisogno di qualcosa di simile a RemoveAt()ma per un array normale.


1
Usa System.Collections.ObjectModel.Collection<Foo>.
abatishchev,

1
Per il mio gioco ho optato per una struttura di dati "null at index". Fondamentalmente, l'array interno (buffer) è di dimensioni statiche e, invece di rimuovere l'indice e ridimensionare l'array, faccio semplicemente annullare l'indice. Quando devo aggiungere un elemento, trovo solo il primo indice non nullo e lo inserisco lì. Funziona abbastanza bene, ma ovviamente non per tutto.
Krythic,

Risposte:


202

Se non si desidera utilizzare Elenco:

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

Potresti provare questo metodo di estensione che non ho ancora testato:

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

E usalo come:

Foo[] bar = GetFoos();
bar = bar.RemoveAt(2);

8
Il primo esempio fornito in questa risposta è molto meno efficiente del secondo. Richiede due copie dell'array e uno spostamento di tutto dopo l'indice piuttosto che una copia dell'array selettivo.
Martin Brown,

2
+1 ovviamente, ma possiamo anche usare list anche OR List <Foo> list = new List <Foll> (GetFoos ()); list.Remove (my_foo); list.RemoveAt (2); dove GetFoos () restituirà l'array di Foos !!!!
Shahjapan,

2
La prima riga all'interno del metodo dovrebbe essere "source.Length" anziché "array.Length".
Nelson,

1
Inoltre, tenere presente che qualsiasi variabile che memorizza un riferimento all'array originale continuerà a contenere i dati originali e che qualsiasi confronto di uguaglianza di riferimento tra l'array di origine e l'array di output restituirà un valore negativo.
bkqc,

1
@MartinBrown In realtà, convertire un elenco in \ from e array è molto più lento di una copia di array (che è in grado di copiare i dati alla massima velocità consentita dalla CPU con poche istruzioni ASM). Inoltre, spostare un elenco è molto veloce perché si tratta solo di scambiare alcuni puntatori e rimuovere i dati del nodo (che in questo caso è solo 8 byte [più altri 16 per i puntatori head \ tail]).
krowe2,

66

La natura degli array è che la loro lunghezza è immutabile. Non è possibile aggiungere o eliminare nessuno degli elementi dell'array.

Dovrai creare un nuovo array più corto di un elemento e copiare i vecchi elementi nel nuovo array, escludendo l'elemento che desideri eliminare.

Quindi è probabilmente meglio usare un elenco anziché un array.


4
Converti l'array in un elencoList<mydatatype> array = new List<mydatatype>(arrayofmydatatype)
Immortal Blue

1
@ImmortalBlue o semplicemente var myList = myArray.ToList();usando il Enumerable.ToList()metodo dallo System.Linqspazio dei nomi.
Dyndrilliac,

58

Uso questo metodo per rimuovere un elemento da un array di oggetti. Nella mia situazione, i miei array sono di lunghezza ridotta. Quindi, se si dispone di array di grandi dimensioni, potrebbe essere necessaria un'altra soluzione.

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

7
Personalmente, mi piace questa risposta meglio della risposta accettata. Dovrebbe essere altrettanto efficiente ed è molto più facile da leggere. Posso guardarlo e sapere che è corretto. Dovrei testare l'altro per assicurarmi che quelle copie siano state scritte correttamente.
Oillio,

1
È davvero un peccato che questa risposta sia così bassa, quando è di gran lunga migliore delle due sopra.
Sepolcritudine

Aaarhg, questa è la risposta che stavo cercando! Questo è il metodo migliore senza elenchi.
Jordi Huertas,

47

Soluzione LINQ a una riga:

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

In 1questo esempio è l'indice dell'elemento da rimuovere - in questo esempio, per la domanda originale, il 2 ° elemento (con 1il secondo elemento nell'indicizzazione di array basato su zero C #).

Un esempio più completo:

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

Dopo aver eseguito lo snippet, il valore di myArraysarà { "a", "c", "d", "e" }.


1
Per le aree che richiedono un accesso frequente / ad alte prestazioni, LINQ non è raccomandato.
Krythic,

3
@Krythic Questo è un commento giusto. Eseguita migliaia di volte in un ciclo ristretto, le prestazioni di questa soluzione non sono buone come alcune delle altre soluzioni più votate in questa pagina: dotnetfiddle.net/z9Xkpn
Jon Schneider,

9

Questo è un modo per eliminare un elemento dell'array, a partire da .Net 3.5, senza copiarlo in un altro array - usando la stessa istanza dell'array 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);
}

2
"senza copiare ad un altro array" - per la documentazione collegata, Array.Resize in realtà fa allocare un nuovo array dietro le quinte, e copia gli elementi dalla vecchia serie a quello nuovo. Tuttavia, mi piace la concisione di questa soluzione.
Jon Schneider,

Molto bello e chiaro se sei sicuro che sia un array relativamente piccolo.
Darren,

1
Continuando il commento di @ JonSchneider, non è "la stessa istanza di array". Ecco perché è necessario utilizzare refquando si chiama il Resizemetodo. La lunghezza di un'istanza di array è fissa e immutabile.
Jeppe Stig Nielsen,

2
Se l'ordine degli elementi non è importante, invece di spostare tutti gli elementi verso il basso, è possibile scambiare l'elemento in corrispondenza dell'indice con l'ultimo elemento e quindi ridimensionare: arr [index] = arr [arr.Length - 1]; Array.Resize (ref arr, arr.Length - 1);
Bartel,

5

Ecco una vecchia versione che ho che funziona sulla versione 1.0 del framework .NET e non necessita di tipi generici.

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

Questo è usato in questo modo:

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

3

Non è esattamente il modo di procedere, ma se la situazione è banale e apprezzi il tuo tempo, puoi provare questo per tipi nulli.

Foos[index] = null

e successivamente verifica la presenza di voci nulle nella tua logica.


È così che l'ho fatto per il mio gioco. Utilizza i buffer nullable per le aree che vengono cambiate molto frequentemente.
Krythic,

2

Come al solito, sono in ritardo alla festa ...

Vorrei aggiungere un'altra opzione al simpatico elenco di soluzioni già presente. =)
Vorrei che questa fosse una buona opportunità per le estensioni.

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

Quindi, definiamo una classe statica e in essa il nostro Metodo.
Dopo ciò, possiamo usare il nostro metodo esteso volenti o nolenti. =)

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"
             */
        }
    }
}

2

Prova sotto il codice:

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

o

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

1

Ecco come l'ho fatto ...

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

1

In un array normale devi mescolare tutte le voci dell'array sopra 2 e quindi ridimensionarlo usando il metodo Resize. Potresti stare meglio usando una ArrayList.


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

0

Ecco una piccola raccolta di metodi di supporto che ho prodotto sulla base di alcune delle risposte esistenti. Utilizza estensioni e metodi statici con parametri di riferimento per la massima idealità:

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

Per quanto riguarda le prestazioni, è decente, ma probabilmente potrebbe essere migliorato. Removesi affida IndexOfe viene creato un nuovo array per ogni elemento che si desidera rimuovere chiamando RemoveAt.

IndexOfè l'unico metodo di estensione in quanto non è necessario restituire l'array originale. Newaccetta più elementi di un certo tipo per produrre un nuovo array di detto tipo. Tutti gli altri metodi devono accettare l'array originale come riferimento, quindi non è necessario assegnare il risultato in seguito poiché ciò già avviene internamente.

Avrei definito un Mergemetodo per unire due matrici; tuttavia, ciò può essere già realizzato con il Addmetodo passando un array effettivo rispetto a più singoli elementi. Pertanto, Addpossono essere utilizzati nei seguenti due modi per unire due serie di elementi:

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

O

Arr.Add<string>(ref myArray, anotherArray);

-1

So che questo articolo ha dieci anni e quindi probabilmente è morto, ma ecco cosa proverei a fare:

Utilizzare il metodo IEnumerable.Skip (), presente in System.Linq . Salterà l'elemento selezionato dall'array e restituirà un'altra copia dell'array che contiene solo tutto tranne l'oggetto selezionato. Quindi ripeti l'operazione per ogni elemento che desideri rimuovere e successivamente salvalo in una variabile.

Ad esempio, se abbiamo una matrice denominata "Esempio" (di tipo int []) con 5 numeri. Vogliamo rimuovere il secondo, quindi provando "Sample.Skip (2);" dovrebbe restituire lo stesso array ad eccezione del 2o numero.


Questo metodo non ignora solo un numero specificato di elementi in una sequenza e quindi restituisce gli elementi rimanenti ? Nel tuo esempio "salterai" i primi due elementi dell'elenco generico e non solo il secondo!
xnr_z,

-4

Primo passo
È necessario convertire l'array in un elenco, è possibile scrivere un metodo di estensione come questo

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

Secondo passaggio
Scrivere un metodo di estensione per riconvertire l'elenco in un array

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

Ultimi passi
Scrivi il tuo metodo finale, ma ricorda di rimuovere l'elemento all'indice prima di riconvertirlo in un array come il code show

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

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

esempi codici potrebbero essere trovati sul mio blog , continua a tenere traccia.


13
Questo è leggermente pazzo considerando l'esistenza di .ToArray()un List<T>costruttore che prende una sequenza esistente ...
user7116
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.