C#:如何在具有多种条件(过滤器和项目组)的列表上迭代
假设我有一个INT列表:C#:如何在具有多种条件(过滤器和项目组)的列表上迭代,c#,algorithm,conditional-statements,C#,Algorithm,Conditional Statements,假设我有一个INT列表: var numbers = new List<int> { 0, 0, 0, 27, 29, 24, 35, 33, 32, 1, 1, 1, 22, 55, 44, 44, 55, 59, 0, 0, 0, 0 }; var number=新列表{0,0,0,27,29,24,35,33,32,1,1,1,22,55, 44, 44, 55, 59, 0, 0, 0, 0 }; 我想实现下面描述的搜索算法。我在找59号 一些未指定的条件决定了我
var numbers = new List<int> { 0, 0, 0, 27, 29, 24, 35, 33, 32, 1, 1, 1, 22, 55,
44, 44, 55, 59, 0, 0, 0, 0 };
var number=新列表{0,0,0,27,29,24,35,33,32,1,1,1,22,55,
44, 44, 55, 59, 0, 0, 0, 0 };
我想实现下面描述的搜索算法。我在找59号
- 2.1。应忽略前导0
- 2.2。如果下一个项目具有相同的十位数,则将它们分组在一起。例如,27、29、24被分组在一起。接下来,将35、33、32分组在一起。接下来,55。等等
- 2.3。如果组中包含偶数,则忽略它,然后继续下一个,直到找到只包含奇数的组。这个组也被忽略,我们继续这个算法的第3步
- 2.3。1也被忽略
44、44、55、59、0、0、0、0
):
- 3.1。我们正在寻找第一组包含以9结尾的项。我们退回这件物品。返回59。如果我们从另一个方向迭代,就会找到29个
- 我可以使用
而
或
来迭代整个列表,但有时我必须从列表的末尾开始,这会导致索引代码混乱。我曾想过实现一个自定义的
来隐藏这个混乱,但在这种情况下,我应该使用IEnumerable/IEnumerator
语句。然而,我想当我试图处理上述foreach
中描述的组时,我仍然会有一团混乱foreach
- 我应该如何迭代列表并同时构建这些组。在C#中,什么是干净的方法
- 出于效率的考虑,我们不应该先过滤掉列表中的所有0。在本例中,该列表可能是1000000000个元素的很长列表的开始。如果我们要查找的数字是第15个,则无需检查第9918477个元素
- 此外,该算法有两个不同的部分(序列的开始和结束)。我不知道如何在一次迭代中处理这两个问题
注意:这个例子不是家庭作业。这是一个简化的问题,旨在删除涉及复杂对象和条件的实际问题的不必要细节。您可以使用Linq,并进行一些自定义。您需要实现一个反向枚举器,以便可以保持主逻辑不变,而不管您是从左向右迭代还是从左向右迭代。不要对大列表使用Linq的
Reverse
方法,因为这会首先迭代整个集合
我应该如何迭代列表并同时构建这些组。在C#中,什么是干净的方法
最干净的方法是使用Linq的GroupBy
,但这将导致在构建组时对整个列表进行迭代。同样,您可以创建自己的枚举数和扩展方法来包装原始枚举数并返回组,这将更有效,因为您知道分组函数对已排序的元素进行操作
一旦你有了一个可枚举的组,只需一小段时间来过滤掉零,另一小段时间来过滤带有偶数的组,一小段时间来跳过下一个组,然后首先找到一个以9结尾的组,假设你有以下列表:
var numbers = new List<int> { 0, 0, 0, 27, 29, 24, 35, 33, 32, 1, 1, 1, 55, 44, 44, 55, 59, 0, 0, 0, 0 };
要实现可逆的功能,您可以在变量中保留的条件,如下所示:
var startIndex = reversed ? numbers.Count - 1 : 0;
var endIndex = reversed ? 0 : numbers.Count - 1;
var increment = reversed ? -1 : 1;
for (int index = startIndex; index != endIndex; index += incremenet)
{
var currentNumber = numbers[index];
分组有几种方法,但一种方法是使用字典:
var groups = new Dictionary<int, List<int>>();
我不会为您编写其余的代码,但这应该会给您一个非常好的主意如何继续。@Theodor Zoulias使用LINQ给出了答案 以下解决方案更为手动: @John Wu通过使用变量和
for
循环解决了我的索引问题
至于问题的另一部分,即如何在迭代列表时构建组,这应该做到:
// This function finds the next group in numbers, starting at index startIndex.
IList<int> FindNextGroup(IList<int> numbers, int startIndex, bool reversed)
{
var group = new List<int>();
var firstNumberOfGroup = numbers[startIndex];
// See @John Wu's answer
var endIndex = reversed ? 0 : numbers.Count -1;
var increment = reversed ? -1 : 1;
for (int i = startIndex + 1; i != endIndex; i += increment)
{
if (numbers[i] is in same group as firstNumberOfGroup
&& numbers[i] != 0 && numbers[i] != 1 && ...)
group.Add(numbers[i]);
else
return group;
}
}
//此函数以数字形式查找下一个组,从索引startIndex开始。
IList FindNextGroup(IList编号、int startIndex、bool反向)
{
var group=新列表();
var firstNumberOfGroup=数字[startIndex];
//见@John Wu的答案
var endIndex=reversed?0:数字。计数-1;
var增量=反向?-1:1;
对于(int i=startIndex+1;i!=endIndex;i+=increment)
{
如果(数字[i]与firstNumberOfGroup位于同一组中
&&数字[i]!=0和数字[i]!=1和……)
增加(编号[i]);
其他的
返回组;
}
}
如果您熟悉LINQ,并且熟悉编写非内置的LINQ方法,那么这个问题很容易解决
using System.Linq;
var source = new List<int> { 0, 0, 0, 27, 29, 24, 35, 33, 32, 1, 1, 1,
22, 55, 44, 44, 55, 59, 0, 0, 0, 0 };
var result = source
.SkipWhile(n => n == 0) // Leading 0s ignored
.GroupConsecutiveByKey(n => n / 10) // Next items having same tens grouped
.SkipWhile(g => g.Any(n => n % 2 == 0)) // Group containing an even number ignored
.Skip(1) // Next group ignored
.Where(g => !g.All(n => n == 1)) // 1s are ignored as well
.FirstOrDefault(g => g.Any(n => n % 10 == 9)) // Contains item ending in 9
.FirstOrDefault(n => n % 10 == 9); // Item ending in 9
Console.WriteLine($"Result: {result}");
源序列仅枚举一次。唯一缓冲的元素是属于一组具有相同十位数的连续数字的元素。此查询可能会给出元素数量达到天文数字的序列的结果。您有一些自定义GroupBy的示例代码吗?正如您所说,我不想迭代整个列表来构建组。另外,通常GroupBy会将不连续的元素组合在一起,这意味着
20、21、40、28
将产生2个组,而不是我想要的3个组。这是家庭作业吗?不,这是虚构的示例。真实的事物涉及一系列复杂的对象,条件和分组是不同的。我试图从一个更复杂的问题中创建一个非常简单的例子。你用相同的十来分组所有的数字
groups[tens].Add(currentNumber);
// This function finds the next group in numbers, starting at index startIndex.
IList<int> FindNextGroup(IList<int> numbers, int startIndex, bool reversed)
{
var group = new List<int>();
var firstNumberOfGroup = numbers[startIndex];
// See @John Wu's answer
var endIndex = reversed ? 0 : numbers.Count -1;
var increment = reversed ? -1 : 1;
for (int i = startIndex + 1; i != endIndex; i += increment)
{
if (numbers[i] is in same group as firstNumberOfGroup
&& numbers[i] != 0 && numbers[i] != 1 && ...)
group.Add(numbers[i]);
else
return group;
}
}
using System.Linq;
var source = new List<int> { 0, 0, 0, 27, 29, 24, 35, 33, 32, 1, 1, 1,
22, 55, 44, 44, 55, 59, 0, 0, 0, 0 };
var result = source
.SkipWhile(n => n == 0) // Leading 0s ignored
.GroupConsecutiveByKey(n => n / 10) // Next items having same tens grouped
.SkipWhile(g => g.Any(n => n % 2 == 0)) // Group containing an even number ignored
.Skip(1) // Next group ignored
.Where(g => !g.All(n => n == 1)) // 1s are ignored as well
.FirstOrDefault(g => g.Any(n => n % 10 == 9)) // Contains item ending in 9
.FirstOrDefault(n => n % 10 == 9); // Item ending in 9
Console.WriteLine($"Result: {result}");
public static IEnumerable<List<TSource>> GroupConsecutiveByKey<TSource, TKey>(
this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
var comparer = EqualityComparer<TKey>.Default;
TKey prevKey = default;
List<TSource> list = null;
foreach (var item in source)
{
var key = keySelector(item);
if (list == null)
{
list = new List<TSource>();
}
else if (!comparer.Equals(key, prevKey))
{
yield return list;
list = new List<TSource>();
}
list.Add(item);
prevKey = key;
}
if (list != null) yield return list;
}