0

字典中的每个键都有许多整数列表。我需要遍历每个键,并且每次都从列表中获取 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

4

4 回答 4

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
}
于 2012-06-07T08:48:13.660 回答
0

这将为您完成:

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

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

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

于 2012-06-07T08:38:10.893 回答
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());

第二步: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多次调用会给你预期的结果。枚举器本身将保持当前位置。

于 2012-06-07T08:46:22.713 回答
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));
}
于 2012-06-07T08:50:23.710 回答