我在尝试为需要按属性分组的数组开发解决方案时遇到了一些困难,我认为它必须非常容易,但是...

我列出了必须按状态分组的批处理过程中的供应商清单。

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']


结果列表会让我以这种方式将供应商发送到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>> SequentialGroupBy<TSource, TSelector>(this IEnumerable<TSource> source, Func<TSource, TSelector> selector)
{
    if (!source.Any())
        return Enumerable.Empty<IEnumerable<TSource>>();

    var result = new List<List<TSource>>();
    result.Add(new List<TSource> { source.ElementAt(0) });

    foreach (var item in source.Skip(1))
    {
        if (selector(result.Last()[0]).Equals(selector(item)))
            result.Last().Add(item);
        else
            result.Add(new List<TSource> { item });
    }
    return result;
}


并像这样使用它:

var result = collection.SequentialGroupBy(item => item.Status).ToList();


请注意,这仍然消耗了整个集合



将从linq的延后执行中受益的扩展方法的纯linq版本是:

public static IEnumerable<IEnumerable<TSource>> SequentialGroupBy<TSource, TSelector>(this IEnumerable<TSource> source, Func<TSource, TSelector> selector)
{
    if (!source.Any())
        return Enumerable.Empty<IEnumerable<TSource>>();

    var prevSelector = selector(source.First());
    int groupingIndex = 0;

    return source.Select(item =>
    {
        var currentSelector = selector(item);
        if (!prevSelector.Equals(currentSelector))
        {
            prevSelector = selector(item);
            groupingIndex++;
        }
        return new { groupingIndex, currentSelector, item };
    }).GroupBy(item => new { item.groupingIndex, item.currentSelector }, select => select.item);
}

10-01 08:30
查看更多