我有一个Foos列表,我想根据foo.HasBar
进行过滤
Foo也有Baz属性
选择Foo时,应过滤所有具有相同Baz对象的Foos
是否可以使用LINQ查询实现此目的,还是应该使用foreach?
编辑:这是一个示例数据集:
Foo.HasBar = true; Foo.Baz = 1;
Foo.HasBar = true; Foo.Baz = 1;
Foo.HasBar = false; Foo.Baz = 1;
Foo.HasBar = true; Foo.Baz = 2;
Foo.HasBar = false; Foo.Baz = 2;
我想要实现的是,如果已选择Foo.HasBar = true; Foo.Baz = 1;
,则不会执行其他Foo.HasBar = false; Foo.Baz = 2;
上的其他迭代,也不会执行Foo.HasBar = true; Foo.Baz = 2;
上的其他迭代。
以下是我用foreach循环完成它的方法:
var selectedFoos = new List<Foo>();
foreach(var foo in foos)
{
if (selectedFoos.Exists(f => f.Baz == foo.Baz))
continue;
if (foo.HasBar)
selectedFoos.Add(foo);
}
答案 0 :(得分:3)
使用IEnumerable<Foo>.Distinct
,并以高效的方式实现您的equals运算符,其中Baz
属性被选中,如果HasBar
不是Baz
,则忽略&&
属性等于。您可以使用HasBar
执行此操作,因为如果左侧表达式为false,则不会评估右侧表达式。
然后,根据IEnumerable<Foo>.Where
和Foo
进行过滤。
如果您不想使用Equals
运算符弄乱Equals
对象,或者您需要针对不同情况使用不同的IEqualityComparer<Foo>
实现,请实现单独的HasBar
。
这样做的另一个好处是,您可以避免在获取不同值时完全检查IEnumerable<Foo> selectedFoos =
sampleDataSet
.Distinct(new PerformantFooComparer())
.Where(f => f.HasBar);
// ...
private class PerformantFooComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x, Foo y)
{
bool isXNull = x == null;
bool isYNull = y == null;
return isXNull == isYNull
&& isXNull
|| (
x.Baz == y.Baz
// && x.HasBar == y.HasBar
// HasBar commented out to avoid performance overhead.
// It is handled by a Where(foo => foo.HasBar) filter
);
}
public int GetHashCode(Foo obj)
{
if (obj == null)
return 0;
// See: http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode
int hash = 17;
hash = hash * 23 + obj.Baz.GetHashCode();
// HasBar intentionally not hashed
return hash;
}
}
属性。如果你跳过了类本身的检查,它可能会导致细微的错误,因为人们可能期望它们是平等的。但是如果有一个名字很好的自定义比较器,人们就不太可能认为它会确保绝对平等。
以下是一些示例代码:
{{1}}
答案 1 :(得分:1)
var results = from f in Foos where (foo.HasBar) && (foo.Baz equals SelectedBaz) select f;
答案 2 :(得分:1)
var q = from f in foos
group f by f.Baz into g
let tempFoo = g.FirstOrDefault(foo => foo.HasBar)
where tempFoo != null
select tempFoo;
我能想到的最好的(现在)。
let的使用应该避免多次调用FirstOrDefault,因此假设FirstOrDefault的实现在找到结果后不会迭代,那么性能密集的HasBar将不会被调用多次。如果没有使用let,则应在where子句中使用FirstOrDefault。
答案 3 :(得分:0)
从您的代码中,您只需:
foos.Where(f => !foos.Where(f2 => f2.HasBar).Any(s => s.Baz == f.Baz));
答案 4 :(得分:0)
你可以简单地使用
var filtered = from f in foos
where foo.HasBar == true
select f;