c# - 批量分组数组

标签 c# arrays generics

我在尝试为需要按属性分组的数组开发解决方案时遇到困难,我认为这必须非常简单,但是...

我有一个必须在按状态分组的批处理过程中处理的供应商列表。

public class SupplierDetails
{
    public int SupplierId { get; set; }
    public string Status { get; set; }
}

供应商列表将具有不同的状态和 SupplierId,供应商需要按照它们插入列表的顺序进行处理,因此,我需要向流程发送一组具有相同状态的第一批供应​​商,一旦处理完毕,我必须将下一个具有相同状态的供应商发送到该流程,依此类推...

列表的样例

[SupplierId=1,Status='C'],[SupplierId=2,Status='C'],[SupplierId=3,Status='A'],[SupplierId=4,Status='C']

结果列表可以让我以这种方式将 Suppliers 发送到 BatchProcess

BatchProcess([SupplierId=1,Status='C'],[SupplierId=2,Status='C'])
BatchProcess([SupplierId=3,Status='A'])
BatchProcess([SupplierId=4,Status='C'])

另一种解释是......“所以当你有 AAABBBABABA 时,你会想要一组中的前三个,然后是一组中的下三个,然后是一堆单独的项目”感谢@Chris

现在,我开发的解决方案不是很漂亮,但使用了泛型

  public List<IEnumerable<T>> FlattenBatchedArray<T, Y>(IEnumerable<T> arr, Func<T, Y> getPropertyValue)
    {
        if (arr == null || arr.Count() == 0 || getPropertyValue == null)
            return null;

        var listArr = arr.ToArray();
        int indexSkip = 0;
        int indexTake = 0;
        var groupedList = new List<IEnumerable<T>>();

        while (indexTake < listArr.Length)
        {
            indexSkip = indexTake;
            var currentValue = getPropertyValue(listArr.ElementAt(indexSkip));
            indexTake = Array.FindIndex(listArr, indexSkip, x => !getPropertyValue(x).Equals(currentValue));
            if (indexTake == -1)
                indexTake = listArr.Length;
            var next = listArr.Skip(indexSkip).Take(indexTake - indexSkip);
            groupedList.Add(next.ToList());
        }
        return groupedList;
    }

这个函数通过了我需要的所有测试

[TestMethod]
public void TestMethod3()
{
    var listPO = new List<SupplierDetails>();
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 1, Status = "O" });
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 2, Status = "C" });
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 3, Status = "C" });
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 4, Status = "O" });
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 5, Status = "O" });
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 6, Status = "O" });
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 7, Status = "C" });
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 8, Status = "O" });

    var result = FlattenBatchedArray(listPO, (x) => x.Status);
    Assert.AreEqual(result.Count, 5);
    Assert.AreEqual(result[0].Count(), 1);
    Assert.AreEqual(result[1].Count(), 2);
    Assert.AreEqual(result[2].Count(), 3);
    Assert.AreEqual(result[3].Count(), 1);
    Assert.AreEqual(result[4].Count(), 1);
    Assert.AreEqual(result[0].ElementAt(0).SupplierId, 1);
    Assert.AreEqual(result[0].ElementAt(0).Status, "O");
    Assert.AreEqual(result[1].ElementAt(0).SupplierId, 2);
    Assert.AreEqual(result[1].ElementAt(0).Status, "C");
    Assert.AreEqual(result[1].ElementAt(1).SupplierId, 3);
    Assert.AreEqual(result[1].ElementAt(1).Status, "C");
    Assert.AreEqual(result[2].ElementAt(0).SupplierId, 4);
    Assert.AreEqual(result[2].ElementAt(0).Status, "O");
    Assert.AreEqual(result[2].ElementAt(1).SupplierId, 5);
    Assert.AreEqual(result[2].ElementAt(1).Status, "O");
    Assert.AreEqual(result[2].ElementAt(2).SupplierId, 6);
    Assert.AreEqual(result[2].ElementAt(2).Status, "O");
    Assert.AreEqual(result[3].ElementAt(0).SupplierId, 7);
    Assert.AreEqual(result[3].ElementAt(0).Status, "C");
    Assert.AreEqual(result[4].ElementAt(0).SupplierId, 8);
    Assert.AreEqual(result[4].ElementAt(0).Status, "O");

    listPO = new List<SupplierDetails>();
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 1, Status = "O" });
    result = FlattenBatchedArray(listPO, (x) => x.Status);
    Assert.AreEqual(result.Count, 1);

    listPO = new List<SupplierDetails>();
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 1, Status = "O" });
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 2, Status = "O" });
    result = FlattenBatchedArray(listPO, (x) => x.Status);
    Assert.AreEqual(result.Count, 1);
    Assert.AreEqual(result[0].Count(), 2);

    listPO = new List<SupplierDetails>();
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 1, Status = "O" });
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 2, Status = "C" });
    result = FlattenBatchedArray(listPO, (x) => x.Status);
    Assert.AreEqual(result.Count, 2);
    Assert.AreEqual(result[0].Count(), 1);
    Assert.AreEqual(result[1].Count(), 1);
    Assert.AreEqual(result[0].ElementAt(0).SupplierId, 1);
    Assert.AreEqual(result[0].ElementAt(0).Status, "O");
    Assert.AreEqual(result[1].ElementAt(0).SupplierId, 2);
    Assert.AreEqual(result[1].ElementAt(0).Status, "C");

    listPO = new List<SupplierDetails>();
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 1, Status = "O" });
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 2, Status = "O" });
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 3, Status = "C" });
    result = FlattenBatchedArray(listPO, (x) => x.Status);
    Assert.AreEqual(result.Count, 2);
    Assert.AreEqual(result[0].Count(), 2);
    Assert.AreEqual(result[1].Count(), 1);
    Assert.AreEqual(result[0].ElementAt(0).SupplierId, 1);
    Assert.AreEqual(result[0].ElementAt(0).Status, "O");
    Assert.AreEqual(result[0].ElementAt(1).SupplierId, 2);
    Assert.AreEqual(result[0].ElementAt(1).Status, "O");
    Assert.AreEqual(result[1].ElementAt(0).SupplierId, 3);
    Assert.AreEqual(result[1].ElementAt(0).Status, "C");

    listPO = new List<SupplierDetails>();
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 1, Status = "O" });
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 2, Status = "C" });
    listPO.Add(new UnitTestProject2.SupplierDetails() { SupplierId = 3, Status = "C" });
    result = FlattenBatchedArray(listPO, (x) => x.Status);
    Assert.AreEqual(result.Count, 2);
    Assert.AreEqual(result[0].Count(), 1);
    Assert.AreEqual(result[1].Count(), 2);
    Assert.AreEqual(result[0].ElementAt(0).SupplierId, 1);
    Assert.AreEqual(result[0].ElementAt(0).Status, "O");
    Assert.AreEqual(result[1].ElementAt(0).SupplierId, 2);
    Assert.AreEqual(result[1].ElementAt(0).Status, "C");
    Assert.AreEqual(result[1].ElementAt(1).SupplierId, 3);
    Assert.AreEqual(result[1].ElementAt(1).Status, "C");
}

最佳答案

这是一个不会多次枚举输入序列的解决方案:

public static IEnumerable<IEnumerable<TSource>> Batch<TKey, TSource>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
    var buffer = new List<TSource>();

    foreach (var element in source)
    {
        if ((buffer.Count > 0) && !Equals(keySelector(element), keySelector(buffer.Last())))
        {
            yield return buffer.ToArray();
            buffer.Clear();
        }

        buffer.Add(element);
    }

    if (buffer.Count > 0)
        yield return buffer.ToArray();
}

(这也处理其中一个键为空的情况,尽管这可能不是必需的。)

[编辑:在意识到我不需要 prev 变量后我简化了它,因为我可以只使用 buffer.Last()。]

关于c# - 批量分组数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46444680/

相关文章:

c# - 随机 InvalidCastException

c# - 通过泛型访问扩展方法总是匹配最不具体的选项?

Typescript - 实现泛型类型的类实例

java - 在错误消息中捕获#XXX(Java 泛型)

c# - 阻止用于访问资源的危险 IP

c# - 访问私有(private)字段

c# - 无法在 ImageView 异步中加载图像

c - 段错误 - 在 C 中声明和初始化数组

java数组线程安全

ios - 在运行 App 时编辑单元格时更新数组