【问题标题】:LINQ grouping multiple columns with nested collectionLINQ 使用嵌套集合对多列进行分组
【发布时间】:2014-10-31 09:52:32
【问题描述】:

我正在按多列对集合进行分组。如果它们处于同一嵌套级别,则很容易将它们分组:

var groupedAirProducts = airproductsPerClient.GroupBy(ac => new
{
    ac.AirProduct.PassengerNameRecord,
    ac.AirProduct.Flights.First().MarketingAirlineCode,
    ac.AirProduct.Flights.First().FlightNo,
    ac.AirProduct.Flights.First().DepartureDate
})

问题是我不想只按第一个航班分组,但我想包括所有航班。我想做的是:

var groupedAirProducts= airproductsPerClient.GroupBy(ac =>
{
    ac.AirProduct.PassengerNameRecord,
    foreach(var flight in ac.AirProduct.Flights){
        flight.MarketingAirlineCode,
        flight.FlightNo,
        flight.DepartureDate
    }
})

请注意,上面的代码只是一个想法的说明。它不是一个工作/正确的代码。我知道如何做到这一点的唯一方法是非常丑陋和复杂。我想知道是否有使用 LINQ 的好方法,或者解决此问题的简单方法。

编辑: 解释更多预期的内容。作为分组的结果,我希望拥有共享相同PassengerNameRecord 的airProducts 集合,并且属于给定航空产品的航班共享相同的MarketingAirlineCode、FlightNo、DepartureDate。我知道如何通过奉承PassengerNameRecord 的集合来实现它,所以Flights 包含在其中,对它进行分组,所以我有一组共享分组属性的航空产品。然后重建受宠若惊的结构。我希望有一种方法可以通过迭代某个集合来添加 groupBy 属性,或者有一种方法可以合并分组集合 - 如果有一种方法可以让集合按 PassengerNameRecord 分组,集合按 Flight 属性分组并合并他们在一起,不幸的是,我怀疑是否有一种简单的方法可以进行这种合并。

【问题讨论】:

  • “易于分组”部分无法编译。您需要使用匿名类型。
  • 所以基本上你想按一个集合分组(+PassengerNameRecord)?创建一个使用正确的GetHashCodeEquals 实现IEqualityComparer<T> 的类。 T 是单个airproductsPerClient 的类型。
  • 我不确定您是否了解分组的工作原理 - 它应该将任何给定实体放入一个组中。在您的示例中,给定多个航班,您最终可能会得到应分配同一实体的多个组 - 这不再是分组(因此您可能希望提供更多关于这应该如何工作的信息)。
  • decPL:也许没有办法用 groupBy 语句来做到这一点,我必须引入一些抽象层 - 扁平化结构或引入新属性,以实现我的目标。这就是我问这个问题的原因:)

标签: c# .net linq


【解决方案1】:

您可以为 GroupBy(或其他 LINQ 方法)实现自定义 IEqualityComparer<AirClient>

您可以通过以下方式实现它:

public class AirClientComparer : IEqualityComparer<AirClient>
{
    public bool Equals(AirClient lhs, AirClient rhs)
    {
        if (lhs == null || rhs == null) return false;
        if(object.ReferenceEquals(lhs, rhs)) return true;
        if(lhs.PassengerNameRecord != rhs.PassengerNameRecord) return false;
        if(object.ReferenceEquals(lhs.AirProduct, rhs.AirProduct)) return true;
        if(lhs.AirProduct == null || rhs.AirProduct == null) return false;
        if(object.ReferenceEquals(lhs.AirProduct.Flights , rhs.AirProduct.Flights )) return true;
        if(lhs.AirProduct.Flights.Count !=  rhs.AirProduct.Flights.Count) return false;
        if(lhs.AirProduct.Flights.Count == 0 && rhs.AirProduct.Flights.Count == 0) return true;
        return lhs.AirProduct.Flights.All(f =>
            rhs.AirProduct.Flights.Any(f2 =>
                    f.MarketingAirlineCode == f2.MarketingAirlineCode
                 && f.FlightNo == f2.FlightNo
                 && f.DepartureDate == f2.DepartureDate));
    }

    public int GetHashCode(AirClient obj)
    {
        if(obj.AirProduct == null) return 0;
        int hash = obj.AirProduct.PassengerNameRecord == null 
                  ? 17 : 17 * obj.AirProduct.PassengerNameRecord.GetHashCode();
        unchecked
        {
            foreach(var flight in obj.AirProduct.Flights)
            {
                hash = (hash * 31) + flight.MarketingAirlineCode == null ? 0 : flight.MarketingAirlineCode.GetHashCode();
                hash = (hash * 31) + flight.FlightNo == null ? 0 : flight.FlightNo.GetHashCode();
                hash = (hash * 31) + flight.DepartureDate.GetHashCode();
            }
        }
       return hash;
    }
}

现在您可以在 GroupBy 中使用它:

var groupedAirProducts = airproductsPerClient.GroupBy(ac => new AirClientComparer());

【讨论】:

  • 这是解决问题的好方法。感谢一百万的解决方案。
【解决方案2】:

在这种情况下,您需要从内表开始。 内表也需要对外表的引用,比如你有一个

ICollection&lt;Flight&gt; Flights in AirProduct

您还需要在Flight 中添加一个Airproduct 成员。

然后像这样对您的航班进行分组(伪代码)

AirProduct.Flights.groupby( flight => new{ flight.MarketingAirlineCode,
        flight.FlightNo,
        flight.DepartureDate,
        flight.product.PassengerNameRecord
});

【讨论】:

    猜你喜欢
    • 2017-04-02
    • 1970-01-01
    • 1970-01-01
    • 2017-12-15
    • 1970-01-01
    • 1970-01-01
    • 2010-09-05
    • 2014-07-08
    • 1970-01-01
    相关资源
    最近更新 更多