如何遍历字典>每次返回每个列表的部分值

本文关键字:返回 列表 List 遍历 何遍历 字典 MyEnum int | 更新日期: 2023-09-27 18:36:30

字典中的每个键都有许多整数的列表。我需要遍历每个键,每次从列表中获取 n 个项目并执行此操作,直到我遍历所有列表中的所有项目。实施它的最佳方法是什么?是否需要实现一些枚举器?

代码:

enum ItemType { Type1=1, Type2=2, Type3=3 };
var items = new Dictionary<ItemType, List<int>>();
items[ItemType.Type1] = new List<int> { 1, 2, 3, 4, 5 };
items[ItemType.Type2] = new List<int> { 11, 12, 13, 15 };
items[ItemType.Type3] = new List<int> { 21, 22, 23, 24, 25, 26 };

例如:n=2。

  1. 第一次迭代返回 1,2,11,12,21,22
  2. 第二次迭代返回 3,4,13,15,23,24
  3. 第三次迭代返回 5,25,26

已更新:最后,我必须按以下顺序获取此项目的列表:1,2,11,12,21,22,3,4,13,15,23,24,5,25,26

如何遍历字典<MyEnum,List<int>>每次返回每个列表的部分值

以下是完成它的方法:

enum ItemType { Type1 = 1, Type2 = 2, Type3 = 3 };
Dictionary<ItemType, List<int>> items = new Dictionary<ItemType, List<int>>();
items[ItemType.Type1] = new List<int> { 1, 2, 3, 4, 5 };
items[ItemType.Type2] = new List<int> { 11, 12, 13, 15 };
items[ItemType.Type3] = new List<int> { 21, 22, 23, 24, 25, 26 };
// Define upper boundary of iteration
int max = items.Values.Select(v => v.Count).Max();
int i = 0, n = 2;
while (i + n <= max)
{
    // Skip and Take - to select only next portion of elements, SelectMany - to merge resulting lists of portions
    List<int> res = items.Values.Select(v => v.Skip(i).Take(n)).SelectMany(v => v).ToList();
    i += n;
    // Further processing of res
}
无需

定义自定义枚举器,只需手动使用该MoveNext

步骤1,将Dictionary<ItemType, List<int>>转换为Dictionary<ItemType, List<IEnumerator<int>>

var iterators = items.ToDictionary(p => p.Key, p => (IEnumerator<int>)p.Value.GetEnumerator());

第 2 步:手动处理MoveNext

public List<int> Get(Dictionary<ItemType, IEnumerator<int>> iterators, int n)
{
    var result = new List<int>();
    foreach (var itor in iterators.Values)
    {
        for (var i = 0; i < n && itor.MoveNext(); i++)
        {
            result.Add(itor.Current);
        }
    }
    return result;
}

多次调用Get将给你预期的结果。枚举器本身将保持当前位置。

这将为您完成:

var resultList = new List<int>();
items.ToList().ForEach(listInts => resultList.AddRange(listInts.Take(n));

这让 LINQ 扩展为您完成艰苦的工作。Take() 将尽可能多地获取,而不会引发异常,如果您请求的比请求的多。在这种情况下,我将结果添加到另一个列表中,但您也可以轻松地在Take()末尾标记另一个 ForEach() 以迭代结果。

我从示例序列中注意到您正在从 x 个起点检索 n 个项目 - 如果您编辑问题以包括如何确定起点,那么我将调整我的示例。


编辑:

因为您希望每次迭代从每个列表中获取 n 个项目,直到没有更多元素返回,所以这将完成:

class Program
{
    static void Main(string[] args)
    {
        var items = new Dictionary<ItemType, List<int>>();
        items[ItemType.Type1] = new List<int> { 1, 2, 3, 4, 5 };
        items[ItemType.Type2] = new List<int> { 11, 12, 13, 15 };
        items[ItemType.Type3] = new List<int> { 21, 22, 23, 24, 25, 26 };
        int numItemsTaken = 0;
        var resultsList = new List<int>();
        int n = 2, startpoint = 0, previousListSize = 0;
        do
        {
            items.ToList().ForEach(x => resultsList.AddRange(x.Value.Skip(startpoint).Take(n)));
            startpoint += n;
            numItemsTaken = resultsList.Count - previousListSize;
            previousListSize = resultsList.Count;
        } 
        while (numItemsTaken > 0);
        Console.WriteLine(string.Join(", ", resultsList));
        Console.ReadKey();
    }
    enum ItemType { Type1 = 1, Type2 = 2, Type3 = 3 };
}

这是您为数不多的几次使用 do while 循环之一,无论n的大小、列表的大小或列表的数量如何,它都可以工作。

"最佳方法"取决于您的目标,例如可读性或性能。

这是一种方法:

var firstIter = items.Values.SelectMany(list => list.Take(2));
var secondIter = items.Values.SelectMany(list => list.Skip(2).Take(2));
var thirdIter = items.Values.SelectMany(list => list.Skip(4).Take(2));
var finalResult =  firstIter.Concat(secondIter).Concat(thirdIter);

编辑:这是一个更通用的版本:

var finalResult = Flatten(items, 0, 2);
IEnumerable<int> Flatten(
    Dictionary<ItemType, List<int>> items, 
    int skipCount, 
    int takeCount)
{
    var iter = items.Values.SelectMany(list => list.Skip(skipCount).Take(takeCount));
    return
        iter.Count() == 0 ?  // a bit inefficient here
        iter :
        iter.Concat(Flatten(items, skipCount + takeCount, takeCount));
}