通过循环 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 )
我正在尝试从数组中删除项目以过滤掉包含某些特定单词的项目,我遇到了越界异常并且我 运行 不知道如何解决它...请帮助!
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());
}
公平地说,两位回答者都对您的原始问题提供了很好的输入。其中
我认为这两种解决方案都有其优点,但我不明白为什么您会想要像 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 )