2014-05-10 52 views
1

我运行下面的表达式来获得基础上,Id 2个集,并确保总的结果只包括“主动/启用”记录之间的三角洲:找不到为什么collection为bool属性返回false?

//Returns the right records but all IsEnabled = false 
var newRecords = allRecordsFromA.Where(a => !(allRecordsFromB.Any(b => ((b.Id == a.Id) && a.IsEnabled)))).ToList(); 

上述结果返回所有的权利A中的记录不在B,,但是的所有记录均标记为IsEnabled = false。真正令人困惑的部分是我在应用程序的另一部分中使用精确相同的表达式,并且它工程。我找回所有正确的记录,他们都被标记为.IsEnabled == true这对我来说毫无意义。我甚至复制过,它不起作用。

不过,如果我更改为2步过程中,我得到的结果我的愿望:

//Works 
var newRecords = allRecordsFromA.Where(a => !(allRecordsFromB.Any(b => (b.Id == a.Id)))).ToList(); 
var results = newRecords.Where(x => x.IsEnabled).ToList(); 

我想操作的顺序在第一表达是错误的返回与!记录被应用到IsEnabled但我没看到它。在我的第一个表达式中使用IsEnabled == false返回记录的内容不正确?

回答

3

我认为你的括号不是你认为他们之外。具体而言,a.IsEnabledAny声明中,然后被否定。

allRecordsFromB.Any(b => ((b.Id == a.Id) && a.IsEnabled)) 

这给了你与第二个例子截然不同的逻辑。我会将a.IsEnabled移至Where的开头,并删除一些不必要的括号以澄清事情。

var newRecords = allRecordsFromA.Where(a => a.IsEnabled && 
         !allRecordsFromB.Any(b => b.Id == a.Id)).ToList(); 

请注意,如果你有大量的数据集,或者认为以下方法会比较清楚,你可以通过哈希集b.Id S的加快速度。 (或者通过某种连接,在内部会做同样的事情)

var bIds = new HashSet<int>(allRecordsFromB.Select(b => b.Id)); 
var newRecords = allRecordsFromA.Where(a => a.IsEnabled && 
              !bIds.Contains(a.Id)).ToList(); 
+0

非常坚实的答案和非常有用的 - 正是我想要解释的,谢谢。你能解释一下为什么'HashSet'表现更好,它做了什么?是因为我只是在用一组ID而不是完整的打击集合,而这些集合的属性并不是公式的一部分,只是膨胀的东西?有时候我正在处理大集合,所以我可能会这样做。 – atconway

+0

插入你的表达式之后ReSharper提供了它认为甚至是简化的表达式(但是我知道你做过与我的OP直接比较) - 这只是一个简单的表达式:var newRecords = allRecordsFromA.Where( a => a.IsEnabled && allRecordsFromB.All(b => b.Id!= a.Id))。ToList();'有没有评论你是否会这样做? – atconway

+1

@atconway解释一个HashSet:想象一下,如果你打印出一个ID的排序列表,而不是以任何特定方式排序的ID列表。不需要搜索列表中的每个项目,您都可以通过查找附近的区域来快速找到某个数字是否在列表中。这就像在'HashSet'中做一个'Contains',而在列表中则是'Contains'。更详细的/准确的描述可以[在这里找到](http://stackoverflow.com/a/4558802/781792)。 –

2

检查a是否已启用在Any之内,该值为否定。因此,如果条件b.Id == a.Id(对于某些ballRecordsFromB)和a.IsEnabled为真,则元素将而不是作为结果的一部分。如果其中一个是错误的,它将成为结果的一部分。

所以,因为这是你想要的正好相反,你应该移动IsEnabled检查Any.

1

我认为问题的一部分是,你的LINQ的说法是难以阅读和那种失败的目的。你可能要考虑这样的事情:

var collectionA = new List<Record>(); 
var collectionB = new List<Record>(); 
var results = collectionA.Intersect(collectionB).Where(x => x.IsEnabled); 

你必须实现Equals方法:

public class Record 
{ 
    public int Id { get; set; } 
    public bool IsEnabled { get; set; } 

    protected bool Equals(Record other) 
    { 
     return Id == other.Id; 
    } 

    public override bool Equals(object obj) 
    { 
     if (ReferenceEquals(null, obj)) return false; 
     if (ReferenceEquals(this, obj)) return true; 
     if (obj.GetType() != this.GetType()) return false; 
     return Equals((Record) obj); 
    } 

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

或者创建一个比较器和传递到相交方法:

public class RecordComparer : IEqualityComparer<Record> 
{ 
    public bool Equals(Record x, Record y) 
    { 
     return x.Id == y.Id; 
    } 

    public int GetHashCode(Record obj) 
    { 
     return obj.GetHashCode(); 
    } 
} 
+0

我已经看到这个实现,而是使用'Except'并传入'IEqualityComparer '实现。使用'Intersect'代替它有什么区别? – atconway

+0

''除了'现在我认为它可能是正确的方法。我的观点只是存在Set比较的方法,我认为使用它们会使代码易于理解。 –

相关问题