通过循环 c# 从数组中排除项目

exclude items from array through loop c#

我正在尝试从数组中删除项目以过滤掉包含某些特定单词的项目,我遇到了越界异常并且我 运行 不知道如何解决它...请帮助!

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

编辑: 感谢大家的快速回答和解决方案

我想你正在寻找这样的东西:

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

命名空间要求:System.Linq;

您的问题不是 linq .ToArray() 调用,而是文件列表的 foreach() 循环。当循环开始时,它会将内部计数器初始化为文件数组 (5) 的原始大小(在本例中)。当您完成并成功在文件数组中找到匹配项时,您动态地 re-sizing 将其减少到 4。所以现在,循环仍在循环所有 5 个原始计数,这就是您崩溃的地方。

相反,通过 for() 循环文件循环,计数器从最大长度开始并返回。因此,如果您在列表中找到第 3 项的匹配项,则在调整文件数组大小后,您的下一个循环将为 2。当您工作到零时,有效索引 #2 仍然存在。

我 运行 参与的另一件事是有人在每个逗号后加空格的测试。您的数组(如本例中我 hard-coded 一个字符串)将创建您的字符串,例如

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

注意到拆分中字符串的前导空格了吗?这会产生假阴性并跳过(在本例中)image4.png 被删除。

我更新了一个函数来显示您正在尝试的工作实例以及 pre-trimming 字符串被“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());
    }

公平地说,两位回答者都对您的原始问题提供了很好的输入。其中 gives you an idea why it happens, 为您提供了替代解决方案。

我认为这两种解决方案都有其优点,但我不明白为什么您会想要像 DRapp 示例中那样使用 for 循环并一遍又一遍地重新创建同一个数组。

假设您不知道 Tân 在他的回答中描述的替代方案,可能在您的代码中最简单的方法是提取功能并将其概括,使其灵活(并且可重用,但这不是真的很重要)。

要做到这一点,您只需创建一个新方法,returns 您因此 IEnumerable<string>

假设您提取了从数据集中删除现有条目的代码,您可以这样写:

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

然后您可以稍后调用此代码,如下所示:

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

我发现 Tân 提供的代码最容易阅读,它简洁明了并且支持不同的 IEqualityComparer 作为第二个参数。它也是框架的一部分,并且是一种扩展方法,您基本上可以将它用于所有类型,并且只要您将 System.Linq 命名空间添加到您的 usings 中,您就可以在任何地方使用它。

我上面分享的代码没有那个,也不让你选择比较器,所以不太灵活。如果您想要区分大小写的比较,则必须更改代码。如果你想在另一个地方重用代码,你也会遇到同样的问题。

你当然可以像下面这样重写它

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

它具有 System.Linq.Enumerable.Except 方法的所有优点,但在这种情况下,我不明白您为什么要这样做。

在我的代码示例和 Tân 的回答中,如果您的 files 数组多次包含相同的名称,您可能会得到重复的条目,但为此您可以更改它到 files.Distinct().GetWithout( namesArray )files.Distinct().Except( namesArray )