6

リストにいくつのアイテムがあるかわからないまま、リストを分割したいと思います。質問は、リストを固定サイズのチャンクに分割したい人とは異なります。

int[] a = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

値を垂直方向に分割したいのですが。

2つに分割:

-------------------
| item 1 | item 6 |
| item 2 | item 7 |
| item 3 | item 8 |
| item 4 | item 9 |
| item 5 |        |

3に分割:

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

結果はこれですが、私の問題は、値が水平方向に分散されていることです。

| item 1 | item 2 |
| item 3 | item 4 |
| item 5 | item 6 |
| item 7 | item 8 |
| item 9 |        |

また

| item 1 | item 2 | item 3 |
| item 4 | item 5 | item 6 |
| item 7 | item 8 | item 9 |

値を垂直方向に分散し、必要なパーツの数を選択する可能性がある方法はありますか?

実生活では

どのような状況でリストを垂直に分割したいのかを知りたい方のために、私のWebサイトのセクションのスクリーンショットを次に示します。

ここに画像の説明を入力してください

4

3 に答える 3

15

.Take ( .Skip()を使用すると、次のことができます。

int[] a = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

int splitIndex = 4; // or (a.Length / 2) to split in the middle.

var list1 = a.Take(splitIndex).ToArray(); // Returns a specified number of contiguous elements from the start of a sequence.
var list2 = a.Skip(splitIndex).ToArray(); // Bypasses a specified number of elements in a sequence and then returns the remaining elements.

が必要な場合.ToList()の代わりに使用できます。.ToArray()List<int>


編集:

あなたがあなたの質問を少し変えた(多分明確にした)後、私はこれがあなたが必要としたものだと思います:

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

それを使用する:

int[] a = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var lists = a.Split(2);

これにより、次のものが生成されます。

2つに分割:a.Split(2);

| item 1 | item 6 |
| item 2 | item 7 |
| item 3 | item 8 |
| item 4 | item 9 |
| item 5 |        |

3つに分割:a.Split(3);

| item 1 | item 4 | item 7 |
| item 2 | item 5 | item 8 |
| item 3 | item 6 | item 9 |

4つに分割:a.Split(4);

| 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

そして4つに分割:b.Split(4);

| item 1 | item 4 | item 7 | item 9 |
| item 2 | item 5 | item 8 | item 10|
| item 3 | item 6 |        |        |
于 2012-11-21T21:52:09.737 に答える
1

これは非常にうまくトリックを行うようです。おそらくもっと効率的に行うことができますが、これは十分に不可解でした...それははるかに簡単です:

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

よりも:

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

頭を包むのに苦労したので、最初に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];
            }
        }
    }

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

しかし、アイテムの数を知らなければ、それを置く場所を計算する方法はありません。

したがって、少し事前計算を行うことで、LINQを使用して、上記と同じことを実現できます。これには、IEnumerableを2回実行する必要がありますが、それを回避する方法はないようです。秘訣は、各値が割り当てられる行を計算することです。

    //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;
    }
于 2012-11-22T23:12:50.193 に答える
-2

取得する要素の数を指定するために使用.Take(#OfElements)します。

.Firstまた、使用することができます.Last

于 2012-11-21T21:57:57.663 に答える