嵌套集合的OrderBy

本文关键字:OrderBy 集合 嵌套 | 更新日期: 2023-09-27 18:28:31

我正在尝试对这个复杂的对象进行排序:

Order _sut = new Order
{
    OrderDataArray = new[]
    {
        new OrderData
        {
            OrderHeaderArray = new[]
            {
                new OrderHeader
                {
                    SequenceNumber = 1,
                    OrderPositionArray = new[]
                    {
                        new OrderPositions
                        {
                            LineNumber = 3
                        },
                        new OrderPositions
                        {
                            LineNumber = 2
                        },
                        new OrderPositions
                        {
                            LineNumber = 1
                        }
                    }
                }
            }
        }
    }
};

使用代码:

[Fact]
public void Sorts_By_Sequence_Number()
{
    var ordered = _sut.OrderDataArray
        .OrderBy(o => o.OrderHeaderArray
            .OrderBy(a => a.OrderPositionArray
                .OrderBy(p => p.LineNumber)))
        .ToArray();
    _sut.OrderDataArray = ordered;
    OutputHelper(_sut);
}

我不明白为什么这不起作用,这意味着排序例程只是保持LineNumber对象的初始顺序。我用OrderBy尝试了各种方法,但看起来不分类。

编辑

感谢您的回复,两者都是正确的。我接受了poke的回应,因为它提供了关于OrderBy方法内部工作的更详细的信息。基本上,我错过了循环中的赋值,我试图同时对所有对象进行排序。

嵌套集合的OrderBy

您应该考虑OrderBy的作用。它根据您在lambda表达式中确定的值对集合进行排序,然后返回一个可枚举值。

你的外线电话很好:

_sut.OrderDataArray.OrderBy(o => something).ToArray();

您按某个东西排序,然后将结果转换为一个(然后排序)数组。这里有两件事很重要:首先,至少在您的示例中,OrderDataArray中只有一个对象,因此没有排序。其次,它取决于something的返回值如何对这些对象进行排序。

那么,在这种情况下,什么是something?如下所示:

o.OrderHeaderArray.OrderBy(a => somethingElse)

那么,不管somethingElse如何,它会返回什么呢?一个IEnumerable<OrderHeader>。多个可枚举对象如何相互比较?它们并不是真正可比的;他们尤其不会根据内容告诉你任何关于订单的信息(你必须先列举它)。因此,本质上,你通过"其他东西"对OrderHeaderArray进行排序,使用没有告诉你任何关于订单的结果作为对OrderDataArray进行排序的关键。然后,将已排序的OrderHeaderArray丢弃。

您对OrderPositionArray做了同样的操作,只需再深入一层,这将不会有任何用处。唯一实际有用的排序发生在OrderPositionArray本身上,但结果再次被丢弃。


现在,如果您想对结构进行排序,您应该通过将排序后的结构重新组合到数组来正确地进行排序。因此,在某些情况下,您必须执行以下操作:

a.OrderPositionArray = a.OrderPositionArray.OrderBy(p => p.LineNumber).ToArray();

但除了OrderPositionArray本身和OrderHeader之外,您实际上没有任何可以排序的东西(因为您不能真正按照子集合的顺序对集合进行排序)。所以你可以这样解决:

foreach (OrderData data in _sut.OrderDataArray)
{
    foreach (OrderHeader header in data.OrderHeaderArray)
    {
        header.OrderPositionArray = header.OrderPositionArray.OrderBy(p => p.LineNumber).ToArray();
    }
    data.OrderHeaderArray = data.OrderHeaderArray.OrderBy(h => h.SequenceNumber).ToArray();
}

代替Linq,您还可以对数组进行适当的排序,这可能会让它变得更好,因为您没有创建新的内部数组:

var c = Comparer<int>.Default;
foreach (OrderData data in _sut.OrderDataArray)
{
    foreach (OrderHeader header in data.OrderHeaderArray)
    {
        Array.Sort(header.OrderPositionArray, new Comparison<OrderPositions>((x, y) => c.Compare(x.LineNumber, y.LineNumber)));
    }
    Array.Sort(data.OrderHeaderArray, new Comparison<OrderHeader>((x, y) => c.Compare(x.SequenceNumber, y.SequenceNumber)));
}

此处,

var ordered = _sut.OrderDataArray.OrderBy(o => ...

期望Func<OrderData, TKey>,并且将通过比较此函数执行的结果来对值进行排序。

同时,传递另一个OrderBy的结果,即IOrderedEnumerable。这根本没有多大意义。

为了对所有嵌套集合进行排序,可以执行以下操作:

foreach (var orderData in _sut.OrderDataArray)
{
  foreach (var orderHeader in orderData.OrderHeaderArray)
  {
    orderHeader.OrderPositionArray = orderHeader.OrderPositionArray
        .OrderBy(x => x.LineNumber).ToArray();
  }
  orderData.OrderHeaderArray = orderData.OrderHeaderArray
      .OrderBy(x => x.SequenceNumber).ToArray();
}
_sut.OrderDataArray = _sut.OrderDataArray
    .OrderBy(x => ...).ToArray();

它根据其项目的LineNumber对每个OrderPositionArray项目进行排序
它根据标头的SequenceNumber对每个OrderHeaderArray进行排序。

然而,还不清楚您希望如何对_sut.OrderDataArray进行排序——在示例中,它被标记为x => ...
它没有可用于排序的类似属性。