Entfernen Sie das Element eines regulären Arrays

135

Ich habe eine Reihe von Foo-Objekten. Wie entferne ich das zweite Element des Arrays?

Ich brauche etwas Ähnliches, RemoveAt()aber für ein normales Array.

Leora
quelle
1
Verwenden Sie System.Collections.ObjectModel.Collection<Foo>.
Abatishchev
1
Für mein Spiel habe ich eine "null at index" -Datenstruktur verwendet. Grundsätzlich hat das innere Array (Puffer) eine statische Größe, und anstatt den Index zu entfernen und die Größe des Arrays zu ändern, mache ich den Index einfach null. Wenn ich ein Element hinzufügen muss, finde ich einfach den ersten Nicht-Null-Index und platziere ihn dort. Funktioniert ziemlich gut, aber offensichtlich nicht für alles.
Krythic

Antworten:

202

Wenn Sie List nicht verwenden möchten:

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

Sie können diese Erweiterungsmethode ausprobieren, die ich noch nicht getestet habe:

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

Und benutze es wie:

Foo[] bar = GetFoos();
bar = bar.RemoveAt(2);
Andrew Kennan
quelle
8
Das erste Beispiel in dieser Antwort ist viel weniger effizient als das zweite. Es erfordert zwei Array-Kopien und eine Verschiebung von allem nach dem Index anstelle einer selektiven Array-Kopie.
Martin Brown
2
+1 natürlich, aber wir können auch list verwenden ODER List <Foo> list = new List <Foll> (GetFoos ()); list.Remove (my_foo); list.RemoveAt (2); wo GetFoos () das Array von Foos zurückgibt !!!!
Shahjapan
2
In der ersten Zeile der Methode sollte "source.Length" anstelle von "array.Length" stehen.
Nelson
1
Beachten Sie außerdem, dass jede Variable, die einen Verweis auf das ursprüngliche Array speichert, weiterhin die ursprünglichen Daten enthält und dass jeder Vergleich der Referenzgleichheit zwischen dem Array in der Quelle und dem Ausgabearray ein Negativ zurückgibt.
bkqc
1
@MartinBrown Tatsächlich ist das Konvertieren einer Liste in \ from und Array viel langsamer als eine Array-Kopie (die die Daten mit nur wenigen ASM-Anweisungen mit der von der CPU zulässigen Höchstgeschwindigkeit kopieren kann). Außerdem ist das Verschieben einer Liste sehr schnell, da nur einige Zeiger ausgetauscht und die Knotendaten entfernt werden müssen (in diesem Fall nur 8 Byte [plus weitere 16 für die Head \ Tail-Zeiger]).
Krowe2
66

Die Natur von Arrays ist, dass ihre Länge unveränderlich ist. Sie können keine Array-Elemente hinzufügen oder löschen.

Sie müssen ein neues Array erstellen, das ein Element kürzer ist, und die alten Elemente in das neue Array kopieren, mit Ausnahme des Elements, das Sie löschen möchten.

Daher ist es wahrscheinlich besser, eine Liste anstelle eines Arrays zu verwenden.

Sebastian Dietz
quelle
4
Konvertieren Sie das Array in eine ListeList<mydatatype> array = new List<mydatatype>(arrayofmydatatype)
Immortal Blue
1
@ImmortalBlue oder einfach var myList = myArray.ToList();mit der Enumerable.ToList()Methode aus dem System.LinqNamespace.
Dyndrilliac
58

Ich benutze diese Methode, um ein Element aus einem Objektarray zu entfernen. In meiner Situation sind meine Arrays klein. Wenn Sie also große Arrays haben, benötigen Sie möglicherweise eine andere Lösung.

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
quelle
7
Ich persönlich mag diese Antwort besser als die akzeptierte Antwort. Es sollte genauso effizient sein und es ist viel einfacher zu lesen. Ich kann es mir ansehen und weiß, dass es richtig ist. Ich müsste den anderen testen, um sicherzustellen, dass diese Kopien korrekt geschrieben wurden.
Oillio
1
Es ist wirklich eine Schande, dass diese Antwort so niedrig ist, wenn sie bei weitem besser ist als die beiden darüber.
Sepulchritude
Aaarhg, das ist die Antwort, nach der ich gesucht habe! Dies ist die beste Methode ohne Listen.
Jordi Huertas
47

Einzeilige LINQ-Lösung:

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

Das 1in diesem Beispiel ist der Index des zu entfernenden Elements - in diesem Beispiel gemäß der ursprünglichen Frage das zweite Element (wobei 1es das zweite Element bei der C # -Null-basierten Array-Indizierung ist).

Ein vollständigeres Beispiel:

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

Danach Snippet ausgeführt wird , der Wert myArraysein wird { "a", "c", "d", "e" }.

Jon Schneider
quelle
1
Für Bereiche, die einen leistungsstarken / häufigen Zugriff erfordern, wird LINQ nicht empfohlen.
Krythic
3
@ Krythic Das ist ein fairer Kommentar. Die Leistung dieser Lösung wird tausende Male in einer engen Schleife ausgeführt und ist nicht so gut wie bei einigen anderen hoch bewerteten
Jon Schneider
9

Auf diese Weise können Sie ein Array-Element ab .Net 3.5 löschen, ohne es in ein anderes Array zu kopieren. Verwenden Sie dazu dieselbe Array-Instanz mit 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
quelle
2
„ohne auf ein anderes Array zu Kopieren“ - pro verknüpften Dokumentation, Array.Resize tatsächlich tut ein neues Array hinter den Kulissen zuweisen, und kopiert die Elemente aus dem alten Array in den neuen. Trotzdem gefällt mir die Prägnanz dieser Lösung.
Jon Schneider
Sehr schön und klar, wenn Sie sicher sind, dass es sich um ein relativ kleines Array handelt.
Darren
1
Wenn Sie den Kommentar von @ JonSchneider fortsetzen, handelt es sich nicht um "dieselbe Array-Instanz". Aus diesem Grund müssen Sie refbeim Aufrufen der ResizeMethode verwenden. Die Länge einer Array-Instanz ist fest und unveränderlich.
Jeppe Stig Nielsen
2
Wenn die Reihenfolge der Elemente nicht wichtig ist, können Sie das Element am Index gegen das letzte Element austauschen und dann die Größe ändern, anstatt alle Elemente nach unten zu verschieben: arr [index] = arr [arr.Length - 1]; Array.Resize (ref arr, arr.Length - 1);
Bartel
5

Hier ist eine alte Version, die auf Version 1.0 des .NET Frameworks funktioniert und keine generischen Typen benötigt.

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

Dies wird folgendermaßen verwendet:

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
quelle
3

Nicht genau der richtige Weg, aber wenn die Situation trivial ist und Sie Ihre Zeit schätzen, können Sie dies für nullbare Typen versuchen.

Foos[index] = null

und später nach Null-Einträgen in Ihrer Logik suchen.

nawfal
quelle
So habe ich es für mein Spiel gemacht. Verwenden Sie nullfähige Puffer für Bereiche, die sehr häufig geändert werden.
Krythic
2

Wie immer bin ich zu spät zur Party ...

Ich möchte der bereits vorhandenen Liste der netten Lösungen eine weitere Option hinzufügen. =)
Ich würde dies als eine gute Gelegenheit für Erweiterungen sehen.

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

Also definieren wir eine statische Klasse und darin unsere Methode.
Danach können wir unsere erweiterte Methode wohl oder übel anwenden. =)

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
quelle
2

Versuchen Sie den folgenden Code:

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

oder

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

So habe ich es gemacht ...

    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
quelle
1

In einem normalen Array müssen Sie alle Array-Einträge über 2 mischen und dann die Größe mithilfe der Resize-Methode ändern. Möglicherweise ist es besser, eine ArrayList zu verwenden.

gkrogers
quelle
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;
    }
user2884232
quelle
0

Hier ist eine kleine Sammlung von Hilfsmethoden, die ich basierend auf einigen der vorhandenen Antworten erstellt habe. Es verwendet sowohl Erweiterungen als auch statische Methoden mit Referenzparametern für maximale Idealität:

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

In Bezug auf die Leistung ist es anständig, aber es könnte wahrscheinlich verbessert werden. Removeverlässt sich darauf IndexOfund für jedes Element, das Sie durch Aufrufen entfernen möchten, wird ein neues Array erstellt RemoveAt.

IndexOfist die einzige Erweiterungsmethode, da das ursprüngliche Array nicht zurückgegeben werden muss. Newakzeptiert mehrere Elemente eines Typs, um ein neues Array dieses Typs zu erzeugen. Alle anderen Methoden müssen das ursprüngliche Array als Referenz akzeptieren, sodass das Ergebnis nicht später zugewiesen werden muss, da dies bereits intern geschieht.

Ich hätte eine MergeMethode zum Zusammenführen von zwei Arrays definiert. Dies kann jedoch bereits mit der AddMethode erreicht werden, indem ein tatsächliches Array gegenüber mehreren einzelnen Elementen übergeben wird. Daher Addkann auf zwei Arten verwendet werden, um zwei Sätze von Elementen zu verbinden:

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

Oder

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

Ich weiß, dass dieser Artikel zehn Jahre alt und daher wahrscheinlich tot ist, aber ich würde Folgendes versuchen:

Verwenden Sie die IEnumerable.Skip () -Methode in System.Linq . Das ausgewählte Element wird aus dem Array übersprungen und eine weitere Kopie des Arrays zurückgegeben, die nur alles außer dem ausgewählten Objekt enthält. Wiederholen Sie dies dann einfach für jedes Element, das Sie entfernen möchten, und speichern Sie es anschließend in einer Variablen.

Zum Beispiel, wenn wir ein Array mit dem Namen "Sample" (vom Typ int []) mit 5 Zahlen haben. Wir wollen den zweiten entfernen und versuchen "Sample.Skip (2);" sollte das gleiche Array zurückgeben, außer ohne die 2. Nummer.

Commandertuna
quelle
Umgeht diese Methode nicht einfach eine bestimmte Anzahl von Elementen in einer Sequenz und gibt dann die verbleibenden Elemente zurück ? In Ihrem Beispiel "überspringen" Sie die ersten beiden Elemente der generischen Liste und nicht nur das zweite!
xnr_z
-4

Erster Schritt
Sie müssen das Array in eine Liste konvertieren. Sie können eine Erweiterungsmethode wie diese schreiben

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

Zweiter Schritt
Schreiben Sie eine Erweiterungsmethode, um die Liste wieder in ein Array zu konvertieren

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

Letzte Schritte
Schreiben Sie Ihre endgültige Methode, aber denken Sie daran, das Element am Index zu entfernen, bevor Sie es wieder in ein Array wie den Code show konvertieren

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

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

Beispielcodes finden Sie in meinem Blog .

Bamara Coulibaly
quelle
13
Dies ist leicht verrückt .ToArray()List<T>
angesichts