C# 如何使用LINQ将列表垂直拆分为n个部分

C# 如何使用LINQ将列表垂直拆分为n个部分,c#,linq,list,C#,Linq,List,我想将一个列表分成几个部分,而不知道该列表中有多少项。这个问题与你们中那些想要将列表分割成固定大小的块的人不同 int[] a = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 我希望这些值被垂直分割 分成两部分: ------------------- | item 1 | item 6 | | item 2 | item 7 | | item 3 | item 8 | | item 4 | item 9 | | item 5 | | 分成

我想将一个列表分成几个部分,而不知道该列表中有多少项。这个问题与你们中那些想要将列表分割成固定大小的块的人不同

int[] a = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
我希望这些值被垂直分割

分成两部分:

-------------------
| item 1 | item 6 |
| item 2 | item 7 |
| item 3 | item 8 |
| item 4 | item 9 |
| item 5 |        |
分成三部分:

| item 1 | item 4 | item 7 |
| item 2 | item 5 | item 8 |
| item 3 | item 6 | item 9 |
分成4部分:

| item 1 | item 4 | item 6 | item 8 |
| item 2 | item 5 | item 7 | item 9 |
| item 3 |        |        |        |
我发现了一些c#扩展可以做到这一点,但它没有按我想要的方式分配值。以下是我的发现:

// this technic is an horizontal distribution
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
{
    int i = 0;
    var splits = from item in list
                    group item by i++ % parts into part
                    select part.AsEnumerable();
    return splits;
}

你知道我怎样才能垂直分布我的值,并且有可能选择我想要的部分数量吗

在现实生活中 对于那些想知道我想在哪种情况下垂直拆分列表的人,这里是我网站一部分的屏幕截图:

使用,您可以:

如果需要
列表,可以使用
.ToList()
而不是
.ToArray()


编辑:

在你稍微改变(澄清)你的问题之后,我想这就是你需要的:

public static class Extensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, int parts)
    {
        var list = new List<T>(source);
        int defaultSize = (int)((double)list.Count / (double)parts);
        int offset = list.Count % parts;
        int position = 0;

        for (int i = 0; i < parts; i++)
        {
            int size = defaultSize;
            if (i < offset)
                size++; // Just add one to the size (it's enough).

            yield return list.GetRange(position, size);

            // Set the new position after creating a part list, so that it always start with position zero on the first yield return above.
            position += size;
        }
    }
}
这将产生:

拆分为2:a.拆分(2)

分成三部分:
a.分成三部分

分成4部分:
a.分成4部分

此外,如果您愿意:

int[] b = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // 10 items
并分成4部分:
b.分成4部分

使用
.Take(#of elements)
指定要获取的元素数


您还可以使用
.First
.Last

这似乎可以很好地完成这个任务。这也许可以做得更有效,但这已经够令人费解的了。。。这样做容易得多:

1|4|7|10
2|5|8
3|6|9
比:

我首先忽略了LINQ请求,因为我很难理解它。使用常规数组操作的解决方案可能会导致如下结果:

    public static IEnumerable<IEnumerable<TListItem>> Split<TListItem>(this IEnumerable<TListItem> items, int parts)
        where TListItem : struct
    {
        var itemsArray = items.ToArray();
        int itemCount = itemsArray.Length;
        int itemsOnlastRow = itemCount - ((itemCount / parts) * parts);
        int numberOfRows = (int)(itemCount / (decimal)parts) + 1;

        for (int row = 0; row < numberOfRows; row++)
        {
            yield return SplitToRow(itemsArray, parts, itemsOnlastRow, numberOfRows, row);
        }
    }

    private static IEnumerable<TListItem> SplitToRow<TListItem>(TListItem[] items, int itemsOnFirstRows, int itemsOnlastRow,
                                                                int numberOfRows, int row)
    {
        for (int column = 0; column < itemsOnFirstRows; column++)
        {
            // Are we on the last row?
            if (row == numberOfRows - 1)
            {
                // Are we within the number of items on that row?
                if (column < itemsOnlastRow)
                {
                    yield return items[(column + 1) * numberOfRows -1];
                }
            }
            else
            {
                int firstblock = itemsOnlastRow * numberOfRows;
                int index;

                // are we in the first block?
                if (column < itemsOnlastRow)
                {
                    index = column*numberOfRows + ((row + 1)%numberOfRows) - 1;
                }
                else
                {
                    index = firstblock + (column - itemsOnlastRow)*(numberOfRows - 1) + ((row + 1)%numberOfRows) - 1;
                }

                yield return
                    items[index];
            }
        }
    }
公共静态IEnumerable拆分(此IEnumerable项,int部分)
其中TListItem:struct
{
var itemsArray=items.ToArray();
int itemCount=itemsArray.Length;
int itemsOnlastRow=itemCount-((itemCount/parts)*parts);
int numberOfRows=(int)(itemCount/(十进制)部分)+1;
for(int row=0;row
LINQ伪代码将是:

//WARNING: DOES NOT WORK
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
{
    int itemOnIndex = 0;
    var splits = from item in list
                 group item by MethodToDefineRow(itemOnIndex++) into row
                 select row.AsEnumerable();
    return splits;
}
//警告:不起作用
公共静态IEnumerable拆分(此IEnumerable列表,整数部分)
{
int itemOnIndex=0;
var splits=来自列表中的项
按MethodToDefineRow(itemOnIndex++)将项分组到行中
选择row.AsEnumerable();
返回分裂;
}
但是如果不知道物品的数量,就无法计算放置物品的位置

因此,通过做一点预计算,您可以使用LINQ实现与上面相同的事情,这需要通过IEnumerable两次,似乎没有办法解决这个问题。诀窍是计算每个值将分配给的行

    //WARNING: Iterates the IEnumerable twice
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
    {
        int itemOnIndex = 0;
        int itemCount = list.Count();
        int itemsOnlastRow = itemCount - ((itemCount / parts) * parts);
        int numberOfRows = (int)(itemCount / (decimal)parts) + 1;
        int firstblock = (numberOfRows*itemsOnlastRow);

        var splits = from item in list
                     group item by (itemOnIndex++ < firstblock) ? ((itemOnIndex -1) % numberOfRows) : ((itemOnIndex - 1 - firstblock) % (numberOfRows - 1)) into row
                     orderby row.Key
                     select row.AsEnumerable();
        return splits;
    }
//警告:迭代IEnumerable两次
公共静态IEnumerable拆分(此IEnumerable列表,整数部分)
{
int itemOnIndex=0;
int itemCount=list.Count();
int itemsOnlastRow=itemCount-((itemCount/parts)*parts);
int numberOfRows=(int)(itemCount/(十进制)部分)+1;
int firstblock=(numberOfRows*itemsOnlastRow);
var splits=来自列表中的项
按(itemOnIndex++
所以您希望将前半部分放在一个列表中,将后半部分放在另一个列表中。你知道列表中有多少元素。。。也许您可以尝试创建一个列表,并将前一半的元素插入到新列表中。。。hmmm..
var list1=a.Take(a.Length/2).ToList();var list2=a.Skip(list1.Count).ToList()您希望拆分为两个相同(类似)长度的列表、固定页面长度还是按内容值拆分?您是否也在寻找处理无限集或固定集合的能力?(显然不可能将无限集一分为二)@MatthewWhited我想将其分成两个长度相同的列表。我已经更新了我的问题,以更准确地显示我想要的。@Oliver乍一看似乎是这样,但OP想要的结果与该问题不同,例如。
。first
只返回一个值(在本例中,如果没有谓词,则返回
1
),而
。Last
也会返回相同的值(除了
| item 1 | item 4 | item 7 |
| item 2 | item 5 | item 8 |
| item 3 | item 6 | item 9 |
| item 1 | item 4 | item 6 | item 8 |
| item 2 | item 5 | item 7 | item 9 |
| item 3 |        |        |        |
int[] b = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // 10 items
| item 1 | item 4 | item 7 | item 9 |
| item 2 | item 5 | item 8 | item 10|
| item 3 | item 6 |        |        |
1|4|7|10
2|5|8
3|6|9
1|4|7|9
2|5|8|10
3|6|
    public static IEnumerable<IEnumerable<TListItem>> Split<TListItem>(this IEnumerable<TListItem> items, int parts)
        where TListItem : struct
    {
        var itemsArray = items.ToArray();
        int itemCount = itemsArray.Length;
        int itemsOnlastRow = itemCount - ((itemCount / parts) * parts);
        int numberOfRows = (int)(itemCount / (decimal)parts) + 1;

        for (int row = 0; row < numberOfRows; row++)
        {
            yield return SplitToRow(itemsArray, parts, itemsOnlastRow, numberOfRows, row);
        }
    }

    private static IEnumerable<TListItem> SplitToRow<TListItem>(TListItem[] items, int itemsOnFirstRows, int itemsOnlastRow,
                                                                int numberOfRows, int row)
    {
        for (int column = 0; column < itemsOnFirstRows; column++)
        {
            // Are we on the last row?
            if (row == numberOfRows - 1)
            {
                // Are we within the number of items on that row?
                if (column < itemsOnlastRow)
                {
                    yield return items[(column + 1) * numberOfRows -1];
                }
            }
            else
            {
                int firstblock = itemsOnlastRow * numberOfRows;
                int index;

                // are we in the first block?
                if (column < itemsOnlastRow)
                {
                    index = column*numberOfRows + ((row + 1)%numberOfRows) - 1;
                }
                else
                {
                    index = firstblock + (column - itemsOnlastRow)*(numberOfRows - 1) + ((row + 1)%numberOfRows) - 1;
                }

                yield return
                    items[index];
            }
        }
    }
//WARNING: DOES NOT WORK
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
{
    int itemOnIndex = 0;
    var splits = from item in list
                 group item by MethodToDefineRow(itemOnIndex++) into row
                 select row.AsEnumerable();
    return splits;
}
    //WARNING: Iterates the IEnumerable twice
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
    {
        int itemOnIndex = 0;
        int itemCount = list.Count();
        int itemsOnlastRow = itemCount - ((itemCount / parts) * parts);
        int numberOfRows = (int)(itemCount / (decimal)parts) + 1;
        int firstblock = (numberOfRows*itemsOnlastRow);

        var splits = from item in list
                     group item by (itemOnIndex++ < firstblock) ? ((itemOnIndex -1) % numberOfRows) : ((itemOnIndex - 1 - firstblock) % (numberOfRows - 1)) into row
                     orderby row.Key
                     select row.AsEnumerable();
        return splits;
    }