我如何 select 将多个列表中的不同且有序的数据转换为通用列表?
How can I select distinct and ordered data into a generic list from multiple lists?
我有几个通用列表,其中包含一些共享数据和一些独特数据。它们包含相同 class 的数据,但使用不同的参数(单位)填充。所以所有的通用列表都是这种类型的: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 不应有重复,但是如果它们有任何不同的值,他们将被视为 unique/distinct。所以提取的数据在排序后应该如下所示:
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() return 重复值?
如果您希望构建另一个仅包含来自所有部分列表的不同数据的列表,那么您可以通过使用 Concat LINQ 方法将它们连接在一起,然后最后使用 Distinct LINQ 方法轻松地做到这一点:
list1.Concat(list2).Concat(list3)...Concat(listN).Distinct();
这假定 类 是值类型。由于您使用的是非常简单的 类,我建议您在其中实现值类型语义,然后这种列表操作将变得微不足道。
添加值类型语义意味着重写 GetHashCode、Equals、添加运算符 == 和运算符 != 并最好用自己的 Equals(T) 实现 IEnumerable。完成所有这些后,正如我所说,列表操作将变得微不足道。
数据排序可以加在最后,如:
list1
.Concat(list2)
.Concat(list3)
...
.Concat(listN)
.Distinct()
.OrderBy(x => x.ItemCode)
.ThenBy(x => x.ShortName)
.ThenBy(x => x.Description);
方法一:修改模型
首先执行 Equals
和 GetHashCode
。然后,您可以 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
的等式来实现 Equals
或 GetHashCode
)
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();
如果您不想打扰 Equals
/ GetHashCode
覆盖或实施 IEqualityComparer<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();
我有几个通用列表,其中包含一些共享数据和一些独特数据。它们包含相同 class 的数据,但使用不同的参数(单位)填充。所以所有的通用列表都是这种类型的: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 不应有重复,但是如果它们有任何不同的值,他们将被视为 unique/distinct。所以提取的数据在排序后应该如下所示:
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() return 重复值?
如果您希望构建另一个仅包含来自所有部分列表的不同数据的列表,那么您可以通过使用 Concat LINQ 方法将它们连接在一起,然后最后使用 Distinct LINQ 方法轻松地做到这一点:
list1.Concat(list2).Concat(list3)...Concat(listN).Distinct();
这假定 类 是值类型。由于您使用的是非常简单的 类,我建议您在其中实现值类型语义,然后这种列表操作将变得微不足道。
添加值类型语义意味着重写 GetHashCode、Equals、添加运算符 == 和运算符 != 并最好用自己的 Equals(T) 实现 IEnumerable。完成所有这些后,正如我所说,列表操作将变得微不足道。
数据排序可以加在最后,如:
list1
.Concat(list2)
.Concat(list3)
...
.Concat(listN)
.Distinct()
.OrderBy(x => x.ItemCode)
.ThenBy(x => x.ShortName)
.ThenBy(x => x.Description);
方法一:修改模型
首先执行 Equals
和 GetHashCode
。然后,您可以 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
的等式来实现 Equals
或 GetHashCode
)
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();
如果您不想打扰 Equals
/ GetHashCode
覆盖或实施 IEqualityComparer<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();