J'essaie de supprimer des éléments du tableau pour filtrer les éléments s'il contient certains mots, je sors de l'exception des limites et je n'ai plus d'idées pour le résoudre ... s'il vous plaît aidez!

string[] files = {
                "image4.png",
                "copy.psd",
                "image3.jpg",
                "image1.png",
                "image2.png",
            };
            string numToRemove = "";
            string[] namesArray = Console.ReadLine().Split(',');
            int num = 0;
            foreach(string t in files){
                Console.WriteLine(t);
                foreach(string s in namesArray){ 
                    if(files[num].Contains(s)){
                        numToRemove = s;
                        Console.WriteLine("exist");
                        files = files.Where(val => val != numToRemove).ToArray();
                    }
                }
                num++;
            };

Edit: Merci à tous pour vos réponses rapides et solutions

1
user78430 19 oct. 2020 à 16:58

3 réponses

Meilleure réponse

Votre problème n'est pas l'appel linq .ToArray (), mais plutôt le cycle foreach () de la liste des fichiers. Lorsque la boucle démarre, elle initialise le compteur interne à la taille ORIGINALE du tableau de fichiers (5) dans ce cas. Au fur et à mesure que vous réussissez à trouver une correspondance dans le tableau des fichiers, vous le redimensionnez dynamiquement à 4. Alors maintenant, la boucle est toujours en train de parcourir les 5 comptages d'origine, ce qui est l'endroit où vous obtenez votre crash.

Au lieu de cela, bouclez les fichiers en boucle par for () avec un compteur commençant par la longueur la plus élevée et remontant. Donc, si vous trouvez une correspondance sur l'élément n ° 3 dans la liste, votre prochain cycle sera 2 après avoir redimensionné le tableau de fichiers. un index valide n ° 2 existera toujours lorsque vous descendez à zéro.

Une autre chose que j'ai rencontrée était un test où quelqu'un met des espaces après chaque virgule. Votre tableau (comme dans cet exemple où j'ai codé en dur une chaîne) créera vos chaînes telles que

namesArray[0] = "copy.psd"
namesArray[1] = " more"
namesArray[2] = " test"
namesArray[3] = " image4.png"

Remarquez les espaces de début des chaînes de la division? Cela donnerait un faux négatif et éviterait (dans cet exemple) image4.png d'être supprimé.

J'ai mis à jour une fonction pour montrer une instance de travail de ce que vous essayez et aussi pré-rogner les chaînes après qu'elles aient été "split ()".

    private void testArray()
    {
        string[] files = {
            "image4.png",
            "copy.psd",
            "image3.jpg",
            "image1.png",
            "image2.png",
        };

        string[] namesArray = "copy.psd, more, test, image4.png".Split(',');
        // pre-trim each string for proper test when comparing strings in arrays
        for (var na = 0; na < namesArray.Length; na++)
            namesArray[na] = namesArray[na].Trim();


        // Now, look for each files to the namesArray
        for ( var outerNum = files.Length -1; outerNum >= 0; outerNum-- )
        {
            var curFileName = files[outerNum];
            Console.WriteLine(curFileName);

            if( namesArray.Contains(curFileName))
            {
                Console.WriteLine(curFileName + " exist");
                files = files.Where(val => val != curFileName).ToArray();
            }
        };

        Console.Write(files.ToString());
    }
0
DRapp 19 oct. 2020 à 14:31

Je pense que vous cherchez quelque chose comme ça:

files = files.Except(namesArray).ToArray();

L'espace de noms requiert: System.Linq;

4
Tân 19 oct. 2020 à 14:04

Pour être juste, les deux répondants donnent une bonne contribution à votre question initiale. Où DRapp vous donne une idée de pourquoi cela se produit, Tân vous offre une solution alternative.

Je pense que les deux solutions ont leur mérite, mais je ne vois pas pourquoi vous voudriez avoir une boucle for et une recréation du même tableau encore et encore comme dans l'exemple de DRapp.

Dites que vous ne connaissez pas l'alternative que Tân a décrite dans sa réponse, probablement la manière la plus simple d'avancer dans votre code serait d'extraire la fonctionnalité et de la généraliser afin qu'elle soit flexible (et réutilisable, mais ce n'est pas vraiment le point).

Pour ce faire, il vous suffit de créer une nouvelle méthode qui vous renvoie un IEnumerable<string> en conséquence.

Supposons que vous extrayez le code qui supprime les entrées existantes de votre ensemble de données, vous pouvez écrire quelque chose comme ceci:

private static IEnumerable<string> GetWithout(IEnumerable<string> entries, IEnumerable<string> toExclude) {
    var excludeSet = new HashSet<string>( toExclude, StringComparer.OrdinalIgnoreCase );
    foreach (var entry in entries) {
        if (excludeSet.Contains( entry )) {
            continue;
        }
        yield return entry;
    }
}

Et vous pouvez ensuite appeler ce code à un moment ultérieur, comme ceci:

public static void Main()
{
    var files = new string[] {
            "image4.png",
            "copy.psd",
            "image3.jpg",
            "image1.png",
            "image2.png",
        };
    var input = "copy.psd, more, test, Image4.png";
    
    var filesWithoutInput = GetWithout( files, input.Split(',').Select( item => item.Trim() ) );
    Console.WriteLine( string.Join(", ", filesWithoutInput ) );
}

Je trouve le code que Tân fournit le plus facile à lire, il est succinct et prend en charge un IEqualityComparer différent comme deuxième argument. Cela fait également partie du framework et est une méthode d'extension, vous pouvez essentiellement l'utiliser pour tous les types, et à condition d'ajouter l'espace de noms System.Linq à vos utilisations, vous pouvez l'utiliser de n'importe où.

Le code que j'ai partagé ci-dessus n'a pas cela, et il ne vous permet pas de choisir le comparateur, il est donc moins flexible. Si vous souhaitez une comparaison sensible à la casse, vous devrez changer le code. Si vous souhaitez réutiliser le code à un autre endroit, vous auriez le même problème.

Vous pouvez bien sûr le réécrire comme suit

internal static class EnumerableExtensions {
    public static IEnumerable<T> GetWithout<T>(this IEnumerable<T> entries, IEnumerable<T> toExclude, IEqualityComparer<T> comparer = default(IEqualityComparer<T>)) {
        var excludeSet = new HashSet<T>( toExclude, comparer );
        foreach (var entry in entries) {
            if (excludeSet.Contains( entry )) {
                continue;
            }
            yield return entry;
        }
    }
}

Ce qui aurait tous les avantages du {{X0} } mais dans ce cas, je ne vois pas pourquoi vous voudriez.

Ce que vous auriez dans mon exemple de code, et dans la réponse de Tân, c'est que vous pourriez obtenir des entrées en double, si votre tableau files contient plus d'une fois le même nom, mais pour cela vous pouvez le changer en {{X1 }} ou files.Distinct().Except( namesArray )

1
Icepickle 20 oct. 2020 à 11:58