从单个列表创建嵌套的对象列表

时间:2019-10-03 12:23:12

标签: c# collections

我有一个项目列表(不确定它们是偶数还是奇数)。我想做的是,从5对记录(实际上是一个列表)中拾取记录,创建另一个列表,然后将这5对列表插入到新列表中。

谢谢

我可以这样做来创建一组物品

MyList
  .Zip(Enumerable.Range(0, MyList.Count()),
       (s, r) => new { 
          Group = r / 5, 
          Item = s })
  .GroupBy(i => i.Group, 
           g => g.Item)
  .ToList();

但是我想生成一个嵌套列表。

4 个答案:

答案 0 :(得分:0)

不确定我是否正确理解了您的目标,但是您可以尝试使用Dictionary:

MyList.Zip(Enumerable.Range(0, MyList.Count()),
  (s, r) => new { Group = r / 5, Item = s })
.GroupBy(i => i.Group, g => g.Item)
.ToDictionary(g => g.Key, g => g.ToList());

答案 1 :(得分:0)

如果您有一系列物品

var items = Enumerable.Range(1, 20);

您想一次服用5张

var setSize = 5;

您可以按索引遍历集合,一次将5个作为列表,然后将所有5个列表放入一个外部列表中

 Enumerable.Range(0, items.Count() - setSize).Select(x => items.Skip(x).Take(setSize).ToList()).ToList()

结果(来自C#交互式shell)看起来像

List<List<int>>(15) { 
    List<int>(5) { 1, 2, 3, 4, 5 }, 
    List<int>(5) { 2, 3, 4, 5, 6 }, 
    List<int>(5) { 3, 4, 5, 6, 7 }, 
    List<int>(5) { 4, 5, 6, 7, 8 }, 
    List<int>(5) { 5, 6, 7, 8, 9 }, 
    List<int>(5) { 6, 7, 8, 9, 10 }, 
    List<int>(5) { 7, 8, 9, 10, 11 }, 
    List<int>(5) { 8, 9, 10, 11, 12 }, 
    List<int>(5) { 9, 10, 11, 12, 13 }, 
    List<int>(5) { 10, 11, 12, 13, 14 }, 
    List<int>(5) { 11, 12, 13, 14, 15 }, 
    List<int>(5) { 12, 13, 14, 15, 16 }, 
    List<int>(5) { 13, 14, 15, 16, 17 }, 
    List<int>(5) { 14, 15, 16, 17, 18 }, 
    List<int>(5) { 15, 16, 17, 18, 19 }
}

如果您希望每个项目在每个列表中仅显示一次,则可以更改以上内容。假设元素数量奇数:

var items = Enumerable.Range(1, 11);

您想要更改用于索引到集合中的初始范围。与其在每个索引上一次取5,不如在每次迭代中将索引向上跳5。唯一棘手的部分是确保在集合除以您要采用的元素数量时进行处理。您不希望最后出现一个空列表。也就是说,这是不正确的:

Enumerable.Range(0, items.Count() / setSize).Select( // don't do this

然后是

Enumerable.Range(0, ((items.Count() - 1) / setSize) + 1).Select(x => items.Skip(setSize * x).Take(setSize).ToList()).ToList();

结果(来自C#交互式shell)看起来像

List<List<int>>(3) {
    List<int>(5) { 1, 2, 3, 4, 5 },
    List<int>(5) { 6, 7, 8, 9, 10 },
    List<int>(1) { 11 }
}

答案 2 :(得分:0)

您似乎想以每批5个项目的形式批量 个元素。 MoreLinq软件包已经为此提供了Batch运算符:

var items=Enumerable.Range(0,17);
var batches=items.Batch(5);

foreach(var batch in batches)
{
   Console.WriteLine(String.Join(" - ",batch));
}

这产生了:

0 - 1 - 2 - 3 - 4
5 - 6 - 7 - 8 - 9
10 - 11 - 12 - 13 - 14
15 - 16

这比分组要快得多,因为它只对集合进行一次迭代。

MoreLINQ还具有其他运算符,例如Window,WindowLeft和WindowRight,它们可生成值的滑动窗口。 items.Window(5)会产生:

0 - 1 - 2 - 3 - 4
1 - 2 - 3 - 4 - 5
...
11 - 12 - 13 - 14 - 15
12 - 13 - 14 - 15 - 16

实施

操作员的实现非常简单,您只需将其复制到项目中即可

public static IEnumerable<IEnumerable<TSource>> Batch<TSource>(this IEnumerable<TSource> source, int size)
{
    return Batch(source, size, x => x);
}

public static IEnumerable<TResult> Batch<TSource, TResult>( IEnumerable<TSource> source, int size,
    Func<IEnumerable<TSource>, TResult> resultSelector)
{
    if (source == null) throw new ArgumentNullException(nameof(source));
    if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size));
    if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));

    return _(); IEnumerable<TResult> _()
    {
        TSource[] bucket = null;
        var count = 0;

        foreach (var item in source)
        {
            if (bucket == null)
            {
                bucket = new TSource[size];
            }

            bucket[count++] = item;

            // The bucket is fully buffered before it's yielded
            if (count != size)
            {
                continue;
            }

            yield return resultSelector(bucket);

            bucket = null;
            count = 0;
        }

        // Return the last bucket with all remaining elements
        if (bucket != null && count > 0)
        {
            Array.Resize(ref bucket, count);
            yield return resultSelector(bucket);
        }
    }
}

代码使用数组来提高效率。如果您确实想使用可变列表,则可以将bucket的类型更改为List<T>,例如:

if (bucket == null)
{
   bucket = new List<TSource>(size); //IMPORTANT: set the capacity to avoid reallocations
}

bucket.Add(item);

...

答案 3 :(得分:0)

为什么不只是GroupBy

 using System.Linq;

 ...

 int groupSize = 5;

 var result = MyList
   .Select((item, index) => new {
      item,
      index 
    })
   .GroupBy(pair => pair.index / groupSize, 
            pair => pair.item)
   .Select(group => group.ToList())
   .ToList();