如何将列表或 IEnumerable 拆分为 N 大小的较小列表?

问题描述 投票:0回答:3

我有很多复选框,目前我想将它们放在 3 列中,每列都有一个 div。

但是我非常任性,明天可能想将它们放入 10 或 20 列,我可能想将其旋转为行,或任何其他类型的 div。

3 列的示例:

<div class="column-1">
    <input type="checkbox" id="foo-1">Checkbox # 1
    <input type="checkbox" id="foo-2">Checkbox # 2
    <input type="checkbox" id="foo-3">Checkbox # 3
</div>
<div class="column-2">
    <input type="checkbox" id="foo-4">Checkbox # 4
    <input type="checkbox" id="foo-5">Checkbox # 5
    <input type="checkbox" id="foo-6">Checkbox # 6
</div>
<div class="column-3">
    <input type="checkbox" id="foo-7">Checkbox # 7
    <input type="checkbox" id="foo-8">Checkbox # 8
    <input type="checkbox" id="foo-9">Checkbox # 9
</div>

我首先尝试使用 3 个 while 循环来做到这一点,如下所示:

@{
    int i = 0;
    IEnumerable<foo> bar = new List<foo>(){foo1,foo2,foo3,foo4,foo5,foo6};
 }
    <div id="column-1">
        while (i < bar.Count() / 3)
        {
        baz = bar.ElementAt(i);
        <input type="checkbox" id="foo-@i" checked='@baz.CheckIt' />@baz.DisplayText
        i++;
        }
    </div>
    <div id="column-2">
        @while (i < bar.Count() * 2 / 3)
        {
        baz = bar.ElementAt(i);
        <input type="checkbox" id="foo-@i" checked='@baz.CheckIt' />@baz.DisplayText
        i++;
        }
    </div>
    <div id="column-3">
        @while (i < bar.Count())
        {
        baz = bar.ElementAt(i);
        <input type="checkbox" id="foo-@i" checked='@baz.CheckIt' />@baz.DisplayText
        i++;
        }
    </div>

这工作正常,但如果我想使用超过 3 列,就会变得非常难看。说实话已经很丑了。它还不允许轻松更改列数。它也可以更改为 for 循环,每个循环从不同的值(分别为 0,1,2)开始,并将

i
增加 3,但这仍然是 3 个循环。

我还尝试使用一些模数运算符在单个循环中执行此操作,如下所示(伪代码):

i = 0
foreach(checkbox in list)
{
    if(i % numberOfColumns == 0)
    {
        createNewRow()
    }
    placeCheckBox()
    i++
}

我认为这更加简洁和可读。然而,这给出了大量的行 div,这并不能很好地满足我的目的。另外还有一个问题,就是如何让复选框位于 div 内部。上面的两个解决方案可以组合起来创建列而不是行,这看起来更好,但仍然存在与第二个解决方案相同的问题。

我觉得必须有一种更简单、更好的方法来做到这一点,而不必对列数进行硬编码,因为没有人愿意编写 20 个基本的 while 循环。

那么有没有一种可扩展且简洁的方法来为分成多个 div 的列表中的每个项目生成 html 标签?如果是的话那是什么?

c# .net razor collections
3个回答
4
投票

这会将 T 的 IEnumerable 拆分为 T 的 IEnumerable 的 IEnumerable。(也许我应该只返回数组。)如果您给它 100 并将其拆分为三组,您将得到 34 组。前 33 组将有 3 个元素,最后一组将有 1 个。

public static IEnumerable<IEnumerable<T>> SplitIntoSetsOfSpecifiedLength<T>
    (this IEnumerable<T> source, int itemsPerSet) // naming is hard
{
    var sourceArray = source as T[] ?? source.ToArray();
    var result = new List<IEnumerable<T>>();
    for (var index = 0; index < sourceArray.Length; index += itemsPerSet)
    {
        result.Add(sourceArray.Skip(index).Take(itemsPerSet));
    }
    return result;
}

为了说明这一点,给定一个从 1 到 20 的数字列表的输入,

var splitIntoThreeLists = oneToTwenty.SplitIntoSetsOfSpecifiedLength(7);

将返回三组:

1   8  15
2   9  16 
3  10  17
4  11  18
5  12  19
6  13  20
7  14

单元测试

[TestMethod]
public void splits_20_into_sets_of_seven()
{
    var values = Enumerable.Range(1, 20);
    var splitValues = values.SplitIntoSetsOfSpecifiedLength(7).ToList();

    // three sets
    Assert.AreEqual(splitValues.Count(), 3); 

    // the first set contains the first seven numbers.
    Assert.IsTrue(splitValues[0].SequenceEqual(Enumerable.Range(1, 7)));

    // the second set contains the next seven numbers
    Assert.IsTrue(splitValues[1].SequenceEqual(Enumerable.Range(8, 7)));

    // the third set contains the last six
    Assert.IsTrue(splitValues[2].SequenceEqual(Enumerable.Range(15, 6)));
}

3
投票

使用相当简单的嵌套循环解决了这个问题(为了清楚起见,也许应该将其更改为

while
)。

这会按顺序遍历列表,每次到达

numberOfColumns
的一小部分时就停止。然后,它创建一个新列并增加分数以获得下一部分的项目。

@{
    int numberOfColumns = 3;
    int currentItemIndex = 0;
    for (int currentColumnIndex = 1; currentColumnIndex <= numberOfColumns; currentColumnIndex++)
    {
        //Create a new column
        <div id="column-@currentColumnIndex">
            //Loop populate column with items
            @while (currentItemIndex < bar.Count() * currentColumnIndex / numberOfColumns)
            {
                baz = bar.ElementAt(currentItemIndex);
                //Add each item
                <input type="checkbox" id="foo-@currentItemIndex" checked='@baz.CheckIt' />@baz.DisplayText
                currentItemIndex++;
            }
        </div>
    }

}

两列的分步示例:

如果你做了

numberOfColumns = 2
,外层循环将执行两次。第一次执行时,内部循环会一直进行,直到
currentItemIndex
等于
bar.Count() * 1 / 2
,或
bar.Count()
的一半,即第一列中项目的一半。然后它迭代到外循环的下一个阶段,将内循环的最大计数器增加到
bar.Count()
,因为计数器仍然设置为
bar.Count()
的一半,这会将项目的后半部分放在第二列中。瞧!


0
投票

从 dotnet 6 开始,您可以使用 Enumerable.Chunk

© www.soinside.com 2019 - 2024. All rights reserved.