如何遍历Dictionary <myenum,list <int =“”>&gt;并且每次返回每个列表的值的一部分?</myenum,>

时间:2012-06-07 08:28:07

标签: c# linq c#-4.0 dictionary

字典中的每个键都有 MANY 整数列表。我需要遍历每个键,每次从列表中取出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. 第3次迭代返回5,25,26
  4. 已更新: 最后,我必须按顺序获得这些项目的清单:1,2,11,12,21,22,3,4,13,15,23,24,5,25,26

4 个答案:

答案 0 :(得分:1)

以下是如何做到的:

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
}

答案 1 :(得分:0)

这将为你做到:

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

这让LINQ扩展为您付出了艰苦的努力。 Take()会尽可能多地使用ForEach(),如果您请求的内容超过了它,则不会抛出异常。在这种情况下,我将结果添加到另一个列表中,但您可以轻松标记Take()末尾的另一个{{3}},以便迭代结果。

我从示例序列中注意到您正在从 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的大小或列表的大小或有多少列表,它都会有效。< / p>

答案 2 :(得分:0)

您不需要定义自定义枚举器,只需手动使用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会给您预期的结果。调查员本身将保持当前的位置。

答案 3 :(得分:0)

“最佳方式”取决于您的目标,例如:可读性或性能。

这是一种方式:

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