将List <t>分解为多个组

时间:2015-09-24 18:29:13

标签: c# asp.net-mvc

我有一个扩展名,可以将List分成几组,这样可以轻松显示内容。这是扩展代码

public static List<List<T>> GroupItems<T>(this List<T> items, int totalGroups)
{
    List<List<T>> groups = null;
    if (items != null)
    {
        groups = new List<List<T>>();
        int itemsPerGroup = (int)Math.Ceiling((double)items.Count / totalGroups);
        if (itemsPerGroup > items.Count)
        {
            List<T> group = new List<T>(items);
            groups.Add(group);
        }
        else
        {

            for (int i = 0; i < totalGroups; i++)
            {
                List<T> group = items.Skip(i * itemsPerGroup).Take(itemsPerGroup).ToList();
                groups.Add(group);
            }
        }
    }

    return groups;
}

以下是该方案:

我有一个列表,它有13个项目(项目可能会有所不同)。

在这个特定的例子中,由于我将列表分成4组,我正在

  • 第1组:第4项
  • 第2组:4项

  • 第3组:4项

  • 第4组:第1项

每组4项来自

int itemsPerGroup = (int)Math.Ceiling((double)items.Count / totalGroups);

现在这是错的,理想情况下我应该

  • 第1组:第4项
  • 第2组:3项
  • 第3组:3项
  • 第4组:3项

同样的,当我尝试将其分成6组时,我得到了

  • 第1组:第3项
  • 第2组:3项
  • 第3组:3项
  • 第4组:3项
  • 第5组:第1项
  • 第6组:0项

你也可以在附图中看到这个

enter image description here

理想情况下应该

  • 第1组:第3项
  • 第2组:2项
  • 第3组:2项
  • 第4组:2项
  • 第5组:2项
  • 第6组:第2项

我在这里缺少什么?我该如何解决这个问题?

7 个答案:

答案 0 :(得分:1)

因为你想要每组中的平均值,并且只要需要它,所以我可以建议这个算法。

public static List<List<T>> GroupItems<T>(this List<T> items, int totalGroups)
    {
        if (items == null || totalGroups == 0) return null;
        var ret = new List<List<T>>();

        var avg = items.Count / totalGroups; //int  rounds towards 0
        var extras = items.Count - (avg * totalGroups);
        for (var i = 0; i < totalGroups; ++i)
            ret.Add(items.Skip((avg * i) + (i < extras ? i : extras)).Take(avg + (i >= extras ? 0 : 1)).ToList());
        return ret;
    }

优点是,它很简单,考虑到O(n ^ 2),就像任何二维算法一样。设计思路是:找到平均值,找到平均值后的N =缺失元素,让前N个元素得到一个额外的元素来填补空白。

答案 1 :(得分:0)

我会每次计算每组的项目。

 int itemsPerGroupCount = 0;
 for (int i = 0; i < totalGroups; i++)
        {
            int itemsPerGroup = (int)Math.Ceiling(
                 ((double)items.Count - itemsPerGroupCount) / (totalGroups - i));

            itemsPerGroupCount += itemsPerGroup;

            List<T> group = items.Skip(itemsPerGroupCount).Take(itemsPerGroup).ToList();
            groups.Add(group);
        }

答案 2 :(得分:0)

我会计算每组的项目(地板而不是天花板):

int itemsPerGroup = (int)Math.Floor((double)items.Count / totalGroups);

然后我会弄清楚剩余物品的数量:

int remainingItems = items.Count % totalGroups

然后在你的循环中:

int itemsToSkip = 0;
for (int i = 0; i < totalGroups; i++)
{
    int itemsPerGroupTemp = itemsPerGroup;
    if (remainingItems != 0) {
        itemsPerGroupTemp++;
        remainingitems--;
    }
    List<T> group = items.Skip(itemsToSkip ).Take(itemsPerGroupTemp).ToList();
    groups.Add(group);
    itemsToSkip += itemsPerGroupTemp;
}

答案 3 :(得分:0)

如何以另一种方式进行, 比如说你有13件你想要6组 所以拿(如int)(13/6)并将其放置,这将给你2.现在有相同数量的组(6)乘以组大小(2)给你12.你有13个项目所以你有剩下一个,将其添加到第一组。应该给你一组nizer。

答案 4 :(得分:0)

如果您将for循环调整为此类

<div class="nav">
  <ul>
    <li><a href="#">Home</a>
    </li>
    <li><a href="#">About</a>
    </li>
    <li><a href="#">Contact</a>
    </li>
    <li><a href="#">Portfolio</a>
    </li>
    <li><a href="#">FAQ</a>
    </li>
  </ul>
</div>

答案 5 :(得分:0)

如果组中各项目的顺序不相关,那么您可以通过按模块分组数来对Linq进行分组。

var result = list.Select((v,i)=>new{Value=v,Index=i})
                 .GroupBy(x => x.Index % numberOfGroups)
                 .Select(grp => grp.Select(x => x.Value).ToList())
                 .ToList();

以下

List<int> list = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
var result =
    list.Select((v, i) => new { Value = v, Index = i })
        .GroupBy(x => x.Index % 6)
        .Select(grp => grp.Select(x => x.Value).ToList())
        .ToList();

int groupNumber = 1;
foreach (var l in result)
{
    Console.WriteLine("Group {0} : {1}", groupNumber++, string.Join(", ", l));
}

会有这些结果

  

第1组:1,7,13

     

第2组:2,8

     

第3组:3,9

     

第4组:4,10

     

第5组:5,11

     

第6组:6,12

答案 6 :(得分:0)

我想你应该做的是计算每个桶的平均物品数量(也就是List),然后将其插入每个桶中,然后插入其余的物体。

注意:保持订单,<int>可以替换为<T>

例如:

public static List<List<int>> ExpandIntoGroups(this List<int> items, int totalGroups)
{
    List<List<int>> expandedList = null;
    if (items != null)
    {
        expandedList = new List<List<int>>();

        // Get amount of items to insert per list (for example: for 14 items, 4 groups, returns {4, 4, 3, 3}
        int[] itemsToInsertPerGroup = GetItemsToInsertPerGroup(items, totalGroups);

        // Go over each group
        for (int currGroup = 0, itemCount = 0;
             currGroup < itemsToInsertPerGroup.Length;
             currGroup++)
        {
            // add the list for the group
            expandedList.Add(new List<int>());

            // Add as much items as needed 
            for (int i = 0;
                 i < itemsToInsertPerGroup[currGroup] && 
                 itemCount < items.Count;
                 i++, itemCount++)
            {
                expandedList[currGroup].Add(items[itemCount]);
            }
        }
    }

    return expandedList;
}

private static int[] GetItemsToInsertPerGroup(List<int> items, int totalGroups)
{
    // Get average amount of items per list
    int avgItemsPerGroup = (int)(items.Count / totalGroups);

    int[] itemsToInsert = new int[totalGroups];

    // Set each cell to be the average
    for (int i = 0; i < itemsToInsert.Length; i++)
    {
        itemsToInsert[i] = avgItemsPerGroup;
    }

    // get how many items remain after inserting average amount
    int remainingItems = items.Count - avgItemsPerGroup * totalGroups;

    // Increase the amount needed per cell for each remaining item
    for (int i = 0, j = 0;
         i < remainingItems;
         ++i, ++j)
    {
        itemsToInsert[j]++;
    }

    return itemsToInsert;
}

当然,这是一种丑陋的做法,但它可以胜任。

一些测试输出:

Total items : 14
Total groups : 4

List 1 has 4 items
List 2 has 4 items
List 3 has 3 items
List 4 has 3 items

List 1 : 1 2 3 4 
List 2 : 5 6 7 8 
List 3 : 9 10 11 
List 4 : 12 13 14