【问题标题】:How can I select distinct and ordered data into a generic list from multiple lists?如何从多个列表中选择不同且有序的数据到通用列表中?
【发布时间】:2016-06-29 18:00:04
【问题描述】:

我有几个通用列表,其中包含一些共享数据和一些唯一数据。它们包含同一类的数据,但使用不同的参数(单位)填充。所以所有的通用列表都是这种类型:List<PriceVarianceData>

我想要对这些通用列表执行的操作是从它们中提取它们包含的不同数据的子集,然后对合并后的列表进行排序。

更具体地说,要查询的列表具有以下结构:

public class PriceVarianceData
{
    public String Unit { get; set; }
    public String ShortName { get; set; }
    public String ItemCode { get; set; }
    public String Description { get; set; }
    public String PriceWeek { get; set; }
    public String Week { get; set; }
    public String Price { get; set; }
    public String Variance { get; set; }
    public String VarianceAverage { get; set; }
    public int RegionOrder { get; set; }
    public int ContractPrice { get; set; }
}

...我要提取数据的通用列表具有以下结构:

public class PriceVarianceSupersetDisplayData
{
    public String ShortName { get; set; }
    public String ItemCode { get; set; }
    public String Description { get; set; }
}

单位将有一些相同的 ShortName + ItemCode + Description 值,我只想要这些值的唯一组合 - ShortName + ItemCode + Description 不应该重复,但是如果它们的任何值不同,它们是被认为是独特的/独特的。因此,提取的数据在排序后应如下所示:

SHORTNAME   ITEMCODE    DESCRIPTION
---------   --------    -----------
Fakeroo     001         Stratoblaster
Fender      001         Stratocaster
Gibson      001         335
Gibson      001         SG
Fender      002         Telecaster
Gibson      002         Les Paul
Carvin      003         Knife
Carvin      003         L6S

我 [认为我] 知道我需要的是 LINQ 查询;在伪代码中,类似于:

List<PriceVarianceSupersetDisplayData> displayDataAmalgamated = select distinct ShortName, ItemCode, Description from craftworksPVDList, chophousePVDList, gordonbierschPVDList, oldchicagoPVDList, oldchifranchisePVDList, rockbottomPVDList order by ItemCode then by ShortName, then by Description

...但不知道如何将其从伪代码转换为真正的 LINQ。

更新

user3185569 和 Zoran Horvat 的答案的组合似乎有效,但我显然没有得到不同的值。我的第一个线索是最终通用列表中的条目数量似乎太多了。

然后我查看了列表中的前两个条目,它们是(或至少看起来是)相同的:

这是我正在使用的代码;如前所述,它是提供的前两个答案的组合:

private List<PriceVarianceSupersetDisplayData> GetSharedDisplayDataForAll()
{
    Func<PriceVarianceData, PriceVarianceSupersetDisplayData> selector =
        (p => new PriceVarianceSupersetDisplayData()
        {
            ShortName = p.ShortName,
            ItemCode = p.ItemCode,
            Description = p.Description
        });

    List<PriceVarianceSupersetDisplayData> displayDataAmalgamated =
            craftworksPVDList.Concat(chophousePVDList)
                            .Concat(chophousePVDList)
                            .Concat(gordonbierschPVDList)
                            .Concat(oldchicagoPVDList)
                            .Concat(oldchifranchisePVDList)
                            .Concat(rockbottomPVDList).Select(selector)
                            .Distinct()
                            .OrderBy(x => x.ItemCode)
                            .ThenBy(x => x.ShortName)
                            .ThenBy(x => x.Description).ToList();

    return displayDataAmalgamated;
}

为什么 Distinct() 返回重复值?

【问题讨论】:

  • 这很容易做到。但首先,craftworksPVDListchophousePVDListgordonbierschPVDList 等...都是同一类型?或者你的意思是结果应该是PriceVarianceSupersetDisplayData
  • @user3185569:是的,您列出的所有内容都是 PriceVarianceData 列表,但其中的“最小化版本”(“PriceVarianceSupersetDisplayData”,可能命名错误)是存储合并结果的位置。跨度>
  • 这更简单,请查看下面的方法。

标签: c# linq generic-list


【解决方案1】:

方法一:修改模型

首先实现EqualsGetHashCode。然后,您可以add 将所有列表合并到一个列表中,select 三个键并使用Distinct 删除重复项,并使用OrderBy - ThenBy 进行排序:

public class PriceVarianceSupersetDisplayData
{
    public String ShortName { get; set; }
    public String ItemCode { get; set; }
    public String Description { get; set; }

    public override bool Equals(object obj)
    {
        var pv = obj as PriceVarianceSupersetDisplayData;

        if (pv == null)
            return false;

        return this.ShortName == pv.ShortName
            && this.ItemCode == pv.ItemCode
            && this.Description == pv.Description;
    }

    public override int GetHashCode()
    {
        return 0;
    }
}

Func<PriceVarianceData, PriceVarianceSupersetDisplayData> selector =
                (p => new PriceVarianceSupersetDisplayData()
                {
                    ShortName = p.ShortName,
                    ItemCode = p.ItemCode,
                    Description = p.Description
                });

List<PriceVarianceSupersetDisplayData> results =
            craftworksPVDList.Concat(chophousePVDList)
                            .Concat(gordonbierschPVDList)
                            .Concat(oldchicagoPVDList)
                            .Concat(oldchifranchisePVDList)
                            .Concat(rockbottomPVDList).Select(selector).Distinct()
                            .OrderBy(x=> x.ItemCode).ThenBy(x=> x.ShortName)
                            .ThenBy(x=> x.Description).ToList();

方法二:不修改模型

(无需使用Tuple的平等实现EqualsGetHashCode

List<PriceVarianceSupersetDisplayData> results = 
        craftworksPVDList.Concat(chophousePVDList)
                        .Concat(gordonbierschPVDList)
                        .Concat(oldchicagoPVDList)
                        .Concat(oldchifranchisePVDList)
                        .Concat(rockbottomPVDList).Select(x=> Tuple.Create(x.ShortName, x.ItemCode, x.Description))
                        .Distinct().OrderBy(x=> x.Item2).ThenBy(x=> x.Item1).ThenBy(x=> x.Item3)
                        .Select(t=> new PriceVarianceSupersetDisplayData()
                        {
                            ShortName = t.Item1,
                            ItemCode = t.Item2,
                            Description = t.Item3
                        }).ToList();

【讨论】:

  • 在类中没有 IEqualityComparer&lt;PriceVarianceSupersetDisplayData&gt;Equals/GetHashCode 覆盖将无法工作
  • @IvanStoev 我先使用了Tuple,然后忘记了这一点而更改了它。现在固定提供两种方法。
  • 方法 2 工作得很好 - 显然没有更多的重复记录,并且从 37,020 条记录减少到 13,419 条记录,所以我很有信心没有。谢谢!
  • 你能帮我解决这个 linq 查询吗:stackoverflow.com/questions/38120664/…
【解决方案2】:

如果您希望构建另一个列表,其中仅包含所有部分列表中的不同数据,那么您可以通过使用 Concat LINQ 方法将它们连接在一起,然后在最后使用 Distinct LINQ 方法来轻松实现:

list1.Concat(list2).Concat(list3)...Concat(listN).Distinct();

这假定类是值类型。由于您使用的是非常简单的类,我建议您在它们中实现值类型语义,然后这种列表操作将变得微不足道。

添加值类型语义意味着覆盖 GetHashCode、Equals,添加 operator == 和 operator != 并最好用自己的 Equals(T) 实现 IEnumerable。正如我所说,完成所有这些后,列表操作将变得微不足道。

可以在最后添加排序数据,比如:

list1
    .Concat(list2)
    .Concat(list3)
    ...
    .Concat(listN)
    .Distinct()
    .OrderBy(x => x.ItemCode)
    .ThenBy(x => x.ShortName)
    .ThenBy(x => x.Description);

【讨论】:

  • 但这只是进入最终列表的列表的一个子集(被查询的包含 11 个成员,而被填充的只有三个。
  • 您希望实施的确切标准是什么?我上面写的查询返回不同值的列表。例如。 list1={1, 2}, list2={2, 3} 将产生列表 {1, 2, 3}。
  • 您是否在数据类中重写了 GetHashCode 和 Equals?不同的方法是同时使用 GetHashCode 和 Equals 来确定哪些对象是重复的。
  • 好的,那就是问题所在;但它适用于提供的更简单的 user3.14 版本。谢谢!
  • 我喜欢在数据类中内置适当的值类型语义,以防止后续出现错误。我不建议在调用端模仿它,因为你有时会忘记这样做,或者你会添加一个字段,然后必须修改所有调用位置,等等。
【解决方案3】:

如果您不想打扰Equals / GetHashCode 覆盖或实现IEqualityComparer&lt;PriceVarianceSupersetDisplayData,这是使Distinct 在其他答案中正常工作所必需的,最简单的方法是使用中间匿名类型投影(因为编译器会自动为匿名类型实现正确的按值比较语义),如下所示:

var displayDataAmalgamated =
    new[] { craftworksPVDList, chophousePVDList, gordonbierschPVDList, oldchicagoPVDList, oldchifranchisePVDList, rockbottomPVDList }
    .SelectMany(list => list.Select(item => new { item.ShortName, item.ItemCode, item.Description }))
    .Distinct()
    .Select(item => new PriceVarianceSupersetDisplayData { ShortName = item.ShortName, ItemCode = item.ItemCode, Description = item.Description })
    .OrderBy(item => item.ShortName).ThenBy(item => item.ItemCode)
    .ToList();

或使用带有group by的查询语法:

var displayDataAmalgamated = (
    from list in new[] { craftworksPVDList, chophousePVDList, gordonbierschPVDList, oldchicagoPVDList, oldchifranchisePVDList, rockbottomPVDList }
    from item in list
    group item by new { item.ShortName, item.ItemCode, item.Description } into g
    orderby g.Key.ShortName, g.Key.Description
    select new PriceVarianceSupersetDisplayData { ShortName = g.Key.ShortName, ItemCode = g.Key.ItemCode, Description = g.Key.Description }
    ).ToList();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-04
    • 2017-03-25
    • 2019-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多