Supprimer l'élément d'un tableau régulier

135

J'ai un tableau d'objets Foo. Comment supprimer le deuxième élément du tableau?

J'ai besoin de quelque chose de similaire à RemoveAt()mais pour un tableau régulier.

Léora
la source
1
Utilisez System.Collections.ObjectModel.Collection<Foo>.
abatishchev
1
Pour mon jeu, je suis allé avec une structure de données "null at index". Fondamentalement, le tableau interne (tampon) est de taille statique, et au lieu de supprimer l'index et de redimensionner le tableau, je rends simplement l'index nul. Quand j'ai besoin d'ajouter un élément, je trouve juste le premier index non nul et je le place là. Fonctionne plutôt bien, mais évidemment pas pour tout.
Krythic

Réponses:

202

Si vous ne souhaitez pas utiliser List:

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

Vous pouvez essayer cette méthode d'extension que je n'ai pas réellement testée:

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

Et utilisez-le comme:

Foo[] bar = GetFoos();
bar = bar.RemoveAt(2);
Andrew Kennan
la source
8
Le premier exemple donné dans cette réponse est beaucoup moins efficace que le second. Il nécessite deux copies de tableau et un décalage de tout après l'index plutôt qu'une copie de tableau sélective.
Martin Brown
2
+1 bien sûr, mais nous pouvons aussi utiliser list OU List <Foo> list = new List <Foll> (GetFoos ()); list.Remove (mon_chot); list.RemoveAt (2); où GetFoos () renverra le tableau de Foos !!!!
shahjapan
2
La première ligne de la méthode doit indiquer «source.Length» au lieu de «array.Length».
Nelson
1
Gardez également à l'esprit que toute variable stockant une référence au tableau d'origine continuera à contenir les données d'origine et que toute comparaison d'égalité de référence entre le tableau de la source et le tableau de sortie renverra un négatif.
bkqc
1
@MartinBrown En fait, la conversion d'une liste en \ from et array est beaucoup plus lente qu'une copie de tableau (qui est capable de copier les données à la vitesse maximale autorisée par le CPU avec seulement quelques instructions ASM). De plus, le décalage d'une liste est très rapide car il s'agit juste d'échanger quelques pointeurs et de supprimer les données du nœud (qui ne font que 8 octets [plus 16 autres pour les pointeurs head \ tail] dans ce cas).
krowe2
66

La nature des tableaux est que leur longueur est immuable. Vous ne pouvez ni ajouter ni supprimer aucun des éléments du tableau.

Vous devrez créer un nouveau tableau d'un élément plus court et copier les anciens éléments dans le nouveau tableau, à l'exclusion de l'élément que vous souhaitez supprimer.

Il est donc probablement préférable d'utiliser une liste au lieu d'un tableau.

Sébastien Dietz
la source
4
Convertir le tableau en listeList<mydatatype> array = new List<mydatatype>(arrayofmydatatype)
Immortal Blue
1
@ImmortalBlue ou simplement en var myList = myArray.ToList();utilisant la Enumerable.ToList()méthode de l' System.Linqespace de noms.
Dyndrilliac
58

J'utilise cette méthode pour supprimer un élément d'un tableau d'objets. Dans ma situation, mes tableaux sont de petite longueur. Donc, si vous avez de grandes baies, vous aurez peut-être besoin d'une autre solution.

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
la source
7
Personnellement, j'aime mieux cette réponse que la réponse acceptée. Il doit être tout aussi efficace et beaucoup plus facile à lire. Je peux le regarder et je sais qu'il est correct. Je devrais tester l'autre pour m'assurer que ces copies étaient écrites correctement.
oillio
1
C'est vraiment dommage que cette réponse soit si basse, alors qu'elle est de loin meilleure que les deux ci-dessus.
Sepulchritude
Aaarhg, c'est la réponse que je cherchais! C'est la meilleure méthode sans listes.
Jordi Huertas
47

Solution LINQ en une ligne:

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

Le 1dans cet exemple est l'index de l'élément à supprimer - dans cet exemple, par la question d'origine, le 2ème élément (avec1 étant le second élément dans l'indexation de tableau de base zéro C #).

Un exemple plus complet:

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

Après avoir exécuté cet extrait de code, la valeur de myArraysera { "a", "c", "d", "e" }.

Jon Schneider
la source
1
Pour les zones nécessitant un accès fréquent / hautes performances, LINQ n'est pas recommandé.
Krythic le
3
@Krythic C'est un commentaire juste. Exécutée des milliers de fois dans une boucle serrée, les performances de cette solution ne sont pas aussi bonnes que certaines des autres solutions les plus appréciées de cette page: dotnetfiddle.net/z9Xkpn
Jon Schneider
9

C'est un moyen de supprimer un élément de tableau, à partir de .Net 3.5, sans copier dans un autre tableau - en utilisant la même instance de tableau avec 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
la source
2
"sans copier dans un autre tableau" - selon la documentation liée, Array.Resize alloue en fait un nouveau tableau dans les coulisses et copie les éléments de l'ancien tableau vers le nouveau. Pourtant, j'aime la concision de cette solution.
Jon Schneider du
Très agréable et clair si vous êtes sûr que c'est un tableau relativement petit.
Darren
1
Poursuivant le commentaire de @ JonSchneider, ce n'est pas "la même instance de tableau". C'est pourquoi vous devez utiliser reflorsque vous appelez la Resizeméthode. La longueur d'une instance de tableau est fixe et immuable.
Jeppe Stig Nielsen
2
Si l'ordre des éléments n'est pas important, au lieu de déplacer tous les éléments vers le bas, vous pouvez permuter l'élément à l'index avec le dernier élément, puis redimensionner: arr [index] = arr [arr.Length - 1]; Array.Resize (ref arr, arr.Length - 1);
Bartel
5

Voici une ancienne version que j'ai qui fonctionne sur la version 1.0 du framework .NET et qui n'a pas besoin de types génériques.

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

Ceci est utilisé comme ceci:

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
la source
3

Ce n'est pas exactement la façon de procéder, mais si la situation est triviale et que vous appréciez votre temps, vous pouvez essayer ceci pour les types Nullable.

Foos[index] = null

et vérifiez plus tard les entrées nulles dans votre logique.

nawfal
la source
C'est ainsi que je l'ai fait pour mon jeu. Optez pour des tampons Nullable pour les zones qui sont modifiées très fréquemment.
Krythic le
2

Comme d'habitude, je suis en retard à la fête ...

J'aimerais ajouter une autre option à la belle liste de solutions déjà présente. =)
Je verrais cela comme une bonne opportunité pour les extensions.

Référence: http://msdn.microsoft.com/en-us/library/bb311042.aspx

Donc, nous définissons une classe statique et en elle, notre méthode.
Après cela, nous pouvons utiliser notre méthode étendue bon gré mal gré. =)

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
la source
2

Essayez le code ci-dessous:

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

ou

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

Voici comment je l'ai fait ...

    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
la source
1

Dans un tableau normal, vous devez mélanger toutes les entrées du tableau au-dessus de 2, puis le redimensionner à l'aide de la méthode Resize. Vous feriez peut-être mieux d'utiliser une ArrayList.

gkrogers
la source
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
la source
0

Voici une petite collection de méthodes d'aide que j'ai produites sur la base de certaines des réponses existantes. Il utilise à la fois des extensions et des méthodes statiques avec des paramètres de référence pour une idéalité maximale:

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 termes de performances, c'est décent, mais cela pourrait probablement être amélioré. Removes'appuie sur IndexOfet un nouveau tableau est créé pour chaque élément que vous souhaitez supprimer en appelant RemoveAt.

IndexOfest la seule méthode d'extension car elle n'a pas besoin de renvoyer le tableau d'origine. Newaccepte plusieurs éléments d'un certain type pour produire un nouveau tableau dudit type. Toutes les autres méthodes doivent accepter le tableau d'origine comme référence, il n'est donc pas nécessaire d'affecter le résultat par la suite, car cela se produit déjà en interne.

J'aurais défini une Mergeméthode pour fusionner deux tableaux; cependant, cela peut déjà être accompli avec la Addméthode en passant un tableau réel par rapport à plusieurs éléments individuels. Par conséquent, Addpeut être utilisé des deux manières suivantes pour joindre deux ensembles d'éléments:

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

Ou

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

Je sais que cet article a dix ans et donc probablement mort, mais voici ce que j'essaierais de faire:

Utilisez la méthode IEnumerable.Skip (), trouvée dans System.Linq . Il sautera l'élément sélectionné du tableau et retournera une autre copie du tableau qui ne contient que tout sauf l'objet sélectionné. Ensuite, répétez simplement cela pour chaque élément que vous souhaitez supprimer et enregistrez-le ensuite dans une variable.

Par exemple, si nous avons un tableau nommé "Sample" (de type int []) avec 5 nombres. Nous voulons supprimer le 2ème, donc en essayant "Sample.Skip (2);" doit renvoyer le même tableau, sauf sans le deuxième numéro.

commandertuna
la source
Cette méthode ne contourne- t-elle pas simplement un nombre spécifié d'éléments dans une séquence, puis retourne les éléments restants ? Dans votre exemple, vous «sauterez» les deux premiers éléments de la liste générique et pas seulement le 2ème!
xnr_z
-4

Première étape
Vous devez convertir le tableau en une liste, vous pouvez écrire une méthode d'extension comme celle-ci

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

Deuxième étape
Écrivez une méthode d'extension pour reconvertir la liste en un tableau

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

Dernières étapes
Écrivez votre méthode finale, mais n'oubliez pas de supprimer l'élément à l'index avant de reconvertir en un tableau comme le code montre

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

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

des exemples de codes pourraient être trouvés sur mon blog , continuez à suivre.

Bamara Coulibaly
la source
13
Ceci est légèrement insensé compte tenu de l'existence .ToArray()et d'un List<T>constructeur qui prend une séquence existante ...
user7116