views:

105

answers:

3

I have a set of model objects that have a public IsVisible boolean property. All I need to do is find if at least one of the set has that value set to TRUE. In other words, if I have 10,000 objects but the second one is true, I don't need to spin through the other 9,998. I already have my answer.

Now I know I could write my own iterating function and break out at the first 'True' value, but I'm hoping that's something LINQ can do. Actually, it doesn't even need to be LINQ. Any suggestions are welcome.

BTW, the language of choice is C#.

Update:

See my last post here. I've added some test code and timings. Seems LINQ is pretty damn poor performance-wise compared to just doing the test myself. Sure it's easier to write, but in mission-critical timings, I'm no longer sure.

What surprised me though is most of the time I've ran these, enumeration won and by a fair clip, but for some reason, when I wrapped the test in multiple passes, it looks to have switched to indexing with a cached count as being fastest.

I also noticed that if I don't reset everything back to 'false', all the remaining/repeated tests seem to be MUCH faster. Somehow, re-setting everything to FALSE (which was purposely overkill to test exactly this...) changes things.

Interesting. Not sure which way I'm gonna go now. This isn't a mission-critical system so perhaps I'll go for readability, but still. Interesting.

+7  A: 

The method you're looking for is Enumerable.Any.

bool anyObjectsVisible = myObjects.Any(myObject => myObject.IsVisible);

This has the exact short-circuiting semantics you are looking for; the sample code is similar to:

static bool AreAnyObjectsVisible(IEnumerable<MyObject> myObjects)
{
    foreach (var myObject in myObjects)
    {
        if (myObject.IsVisible) return true;
    }

    return false;
}
Ani
That is exactly what I'm looking for! (I'm new to LINQ. Can't believe I missed that!) I tried accepting your answer but it's telling me I have to wait 12 minutes. I at least voted it up. BTW, good GOD that was fast! Wasn't even a minute since I posted it! You rock!
MarqueIV
@MarqueIV: Cheers. Here's a great resource to learn LINQ: http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx
Ani
Hey Ari... per your comment, I nuked my testing. BUT... if you want to include something verbose like that but it isn't an answer (i.e. you want code formatting, etc.), is the proper etiquette to edit the original question? (Yes, you're right that in this case it really should have been a separate question, but I'm speaking more generally.)
MarqueIV
@MarqueIV: If it is a 'minor' follow-up question, I would recommend posting a comment. You can include some code in the comments, or link to PasteBin or something if the sample is large. The query that you had (performance of LINQ to objects vs old-school for-loops) was complex enough to warrant its own question, IMO. Don't post follow-up questions as an answer; some users will downvote it mercilessly. Also, you should know that I too am quite new to SO; I suggest posting this query as a question on meta.stackoverflow.com; experienced users will point you in the right direction. Cheers.
Ani
+2  A: 

If you need to actually find an object, simply use the .First or .FirstOrDefault method:

var myObject = myCollection.First(x=>x.IsVisible);

or

var myObject = myCollection.FirstOrDefault(x=>x.IsVisible);

The only difference between them is that the .First method will throw an exception if there is no such object in the collection when the second one returns the default value (null in this example).

If you just need to check if there is any object with this property set, use

var thereIsVisibleObject = myCollection.Any(x=>x.IsVisible);

All of these methods stop iterating through the collection once the corresponding object is found.

Or, should you check if all of the objects are visible/invisible, you may do this:

var allTheObjectsAreVisible = myCollection.All(x=>x.IsVisible);
var allTheObjectsAreInvisible = myCollection.All(x=>!x.IsVisible);

But .All method will enumerate all of the elements (which is obvious from its name).

Patrol02
Voted you up, but Ani got the 'Accepted Answer' because they answered within literally 60 seconds of my posting it. (Even before I had a chance to edit a typo!) But this is a lot of great info too so I voted you up. Thanks!!
MarqueIV
Actually, I just re-read your comments on 'All'. Correct me if I'm wrong, but they will NOT enumerate all of the objects if your predicate returns false as it should just return false immediately, so your comment that they will enumerate everything I'm not sure if it's correct. After all saying 'are Any visible' should short-circuit the same as saying 'are they All not visible' if even one is visible, correct? (The only difference being one returns true, the other false.) Of course I'm basing that on logic and not documentation so I can't say for sure.
MarqueIV
I'm correct. .All() iterates through the collection you provide it with and returns as soon as your predicate is _false_.
Patrol02
So All is the _opposite_ of Any. If you say .All(x=>x.IsVisible) it will iterate through the collection until it meets something _invisible_. Say, if all of the elements are visible, it'll enumerate all of them. But if you use .Any(x=>x.IsVisible), it will iterate only until it meats something _visible_, so if all the elements are visible, it'll returns just after the first element. So, there is associativity: .Any(x=>x.Visible) == .All(x=>x.Invisible). It is a simple match rule.
Patrol02
@Patrol02... I don't think you are correct, however I think you're misunderstanding what I'm challenging. I'm not challenging the result of .All. In your answer you specifically said it '.All will enumerate all of the elements' but you should have continued '...until the predicate is false' which you did say in your comment above. My point was that it will NOT iterate through all objects unless the predicate returns true for everything which is an important thing to understand, especially when whatever your are checking is very heavy, it will make a difference, and why I called it out.
MarqueIV
Just spoke to my buddy and he thinks that .All doesn't have internal short-circuiting like you originally stated, but you then appeared to contradict yourself in your follow-up comment, so I'm not sure what the case is, but that's an easy check. I'll just log in the IsVisible getter of the object. Done and done!
MarqueIV
I just confirmed that All does NOT blindly iterate over everything but will rather short-circuit if the predicate returns false. I created a test with 100 objects and set a property of it to return true for everything except the fifth object which returns false. I put a counter in the getter of that property. I then ran "var result = cxFoo.All(foo => foo.Test);" and checked the read-count at the end and it was five, not 100, proving that 'All' does NOT enumerate "all" objects. Again, I'm not challenging the results. I'm challenging your comment every item gets enumerated which they don't.
MarqueIV
A: 

Here is an ".Any()" implementation:

public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    if (predicate == null)
    {
        throw Error.ArgumentNull("predicate");
    }
    foreach (TSource local in source)
    {
        if (predicate(local))
        {
            return true;
        }
    }
    return false;
}

So here is it, nothing "special" with .Any(), it is just a wrapper around "foreach". Nothing special to test there and nothing to blame in terms of micro-micro-micro optimization.

Patrol02
Your 'micro-micro-micro' optimizations comment aside, it's still interesting that my code, which looks almost identical to what you posted (sans the null checks) ran up to 400% faster than LINQ in some cases. That's what I was referring to.
MarqueIV