自定义类的List.except
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义类的List.except相关的知识,希望对你有一定的参考价值。
假设我有一个自定义类:
public class WineCellar
{
public string year;
public string wine;
public double nrbottles;
}
可以说我现在有一个这个自定义类的列表:
List<WineCellar> orignialwinecellar = List<WineCellar>();
包含这些项目:
2012 Chianti 12
2011 Chianti 6
2012 Chardonay 12
2011 Chardonay 6
我知道,如果我想比较两个列表并返回一个新列表,其中只包含不在其他列表中的项目,我会这样做:
var newlist = list1.Except(list2);
如何将其扩展到自定义类?让我们说:
string[] exceptionwinelist = {"Chardonay", "Riesling"};
我想要退回:
List<WineCellar> result = originalwinecellar.wine.Except(exceptionwinelist);
这个伪代码显然不起作用,但希望能说明我想做的事情。然后,该shoudl返回自定义类winecellar的List,其中包含以下项:
2012年基安蒂12
2011年基安蒂6
谢谢。
你真的不想在这里使用Except
,因为你没有用作黑名单的WineCellar
对象的集合。你所拥有的是一系列规则:“我不想要具有这种和这样的葡萄酒名称的物品”。
因此,最好简单地使用Where
:
List<WineCellar> result = originalwinecellar
.Where(w => !exceptionwinelist.Contains(w.wine))
.ToList();
以人类可读的形式:
我想要所有WineCellars,其中葡萄酒名称不在例外列表中。
顺便说一句,WineCellar
类名称有点误导;那些物品不是酒窖,它们是库存物品。
一种解决方案是使用扩展方法:
public static class WineCellarExtensions
{
public static IEnumerable<WineCellar> Except(this List<WineCellar> cellar, IEnumerable<string> wines)
{
foreach (var wineCellar in cellar)
{
if (!wines.Contains(wineCellar.wine))
{
yield return wineCellar;
}
}
}
}
然后像这样使用它:
List<WineCellar> result = originalwinecellar.Except(exceptionwinelist).ToList();
exceptionWineList
是string[]
但是originalWineCellar
是List<WineCellar>
,WineCellar
不是string
,所以在这些之间执行Except
是没有意义的。
你可以这么容易做到
// use HashSet for look up performance.
var exceptionWineSet = new HashSet<string>(exceptionWineList);
var result = orginalWineCellar.Where(w => !exceptionWineSet.Contains(w.Wine));
我认为你在你的问题中提到的是类似的东西
WineCellar : IEquatable<string>
{
...
public bool Equals(string other)
{
return other.Equals(this.wine, StringComparison.Ordinal);
}
}
这允许你将WineCellar
s等同于string
s。
但是,如果我要修改你的模型,我会想出类似的东西,
enum WineColour
{
Red,
White,
Rose
}
enum WineRegion
{
Bordeaux,
Rioja,
Alsace,
...
}
enum GrapeVariety
{
Cabernet Sauvignon,
Merlot,
Ugni Blanc,
Carmenere,
...
}
class Wine
{
public string Name { get; set; }
public string Vineyard { get; set; }
public WineColour Colour { get; set; }
public WineRegion Region { get; set; }
public GrapeVariety Variety { get; set; }
}
class WineBottle
{
public Wine Contents { get; set; }
public int Millilitres { get; set; }
public int? vintage { get; set; }
}
class Bin : WineBottle
{
int Number { get; set; }
int Quantity { get; set; }
}
class Cellar : ICollection<WineBottle>
{
...
}
然后,你可以看到有几种方法来比较Wine
,我可能想要在Cellar
的一个或多个属性上过滤Wine
。因此,我可能会为自己提供一些灵活性,
class WineComparer : EqualityComparer<Wine>
{
[Flags]
public Enum WineComparison
{
Name = 1,
Vineyard= 2,
Colour = 4,
Region = 8,
Variety = 16,
All = 31
}
private readonly WineComparison comparison;
public WineComparer()
: this WineComparer(WineComparison.All)
{
}
public WineComparer(WineComparison comparison)
{
this.comparison = comparison;
}
public override bool Equals(Wine x, Wine y)
{
if ((this.comparison & WineComparison.Name) != 0
&& !x.Name.Equals(y.Name))
{
return false;
}
if ((this.comparison & WineComparison.Vineyard) != 0
&& !x.Vineyard.Equals(y.Vineyard))
{
return false;
}
if ((this.comparison & WineComparison.Region) != 0
&& !x.Region.Equals(y.Region))
{
return false;
}
if ((this.comparison & WineComparison.Colour) != 0
&& !x.Colour.Equals(y.Colour))
{
return false;
}
if ((this.comparison & WineComparison.Variety) != 0
&& !x.Variety.Equals(y.Variety))
{
return false;
}
return true;
}
public override bool GetHashCode(Wine obj)
{
var code = 0;
if ((this.comparison & WineComparison.Name) != 0)
{
code = obj.Name.GetHashCode();
}
if ((this.comparison & WineComparison.Vineyard) != 0)
{
code = (code * 17) + obj.Vineyard.GetHashCode();
}
if ((this.comparison & WineComparison.Region) != 0)
{
code = (code * 17) + obj.Region.GetHashCode();
}
if ((this.comparison & WineComparison.Colour) != 0)
{
code = (code * 17) + obj.Colour.GetHashCode();
}
if ((this.comparison & WineComparison.Variety) != 0)
{
code = (code * 17) + obj.Variety.GetHashCode();
}
return code;
}
}
这可能看起来很费劲但它有一些用处。让我们说我们想在你的酒窖里除了红里奥哈之外的所有葡萄酒,你可以做点什么,
var comparison = new WineComparer(
WineComparison.Colour + WineComparison.Region);
var exception = new Wine { Colour = WineColour.Red, Region = WineRegion.Rioja };
var allButRedRioja = cellar.Where(c =>
!comparison.Equals(c.Wine, exception));
要直接对泛型类使用此类扩展方法,您应该实现比较器。它由两个方法组成:Equal和GetHashCode。您应该在WineCellar类中实现它们。 Note the second example。
请注意,基于哈希的方法比基本的“List.Contains ...”实现快得多。
我有同样的问题。我尝试了Darren的例子,但无法让它正常工作。
因此,我对Darren的例子进行了如下修改:
static class Helper
{
public static IEnumerable<Product> Except(this List<Product> x, List<Product> y)
{
foreach(var xi in x)
{
bool found = false;
foreach (var yi in y) { if(xi.Name == yi.Name) { found = true; } }
if(!found) { yield return xi; }
}
}
}
这适合我。如果需要,您可以在if子句中添加几个字段。
以上是关于自定义类的List.except的主要内容,如果未能解决你的问题,请参考以下文章