2

I have a method that is supposed to check whether there is exactly one element in a collection that holds true for some predicate (given as a Func).

public bool ExistsUnique(Func<T, bool> p)
    {
        var tempCol = from i in MyCollection where p(i) select i;  
        return (tempCol.Count() == 1);
    }

The problem with this is that when a second element that also holds true for the predicate is found (for example two of the same string exists in a collection) the count is still 1. Which means it either overrides the first element or never adds the second because it already exists.

Any ideas as to how I can fix this method? thx /Peter

1
  • I suspect this SO question and its answers may help you. Commented Nov 8, 2011 at 0:34

4 Answers 4

2

You can use the Single() method provided by LINQ like this:

public bool ExistsUnique(Func<T, bool> p)
{
    try
    {
        var temp = myCollection.Single(x => p(x));
    }
    catch(Exception e)
    {
        // log exception
        return false;
    }

    return true;
}

"Returns the only element of a sequence that satisfies a specified condition, and throws an exception if more than one such element exists."

From http://msdn.microsoft.com/en-us/library/bb535118.aspx

EDIT

To avoid throwing an exception, you may also use the SingleOrDefault() method:

public bool ExistsUnique(Func<T, bool> p)
{
    return myCollection.SingleOrDefault(x => p(x)) != null;
}
Sign up to request clarification or add additional context in comments.

4 Comments

that is a great idea! I tried it though I still get the same problem and it returns true even though i tried adding two of the same string.
Throwing exceptions are pretty slow when they aren't necessary, may want to be careful with this technique when there are easy ways to do the same thing with no exception.
Added an option without exception but left the exception in there as well. Both are viable options in my opinion depending on the desired results.
@shuniar I disagree but -1 removed. BTW when responding, people will know much much quicker if you put an at-theirname in your comment
1

There must be some other problem. I'd suspect your predicate. For example, this returns a count of 2, as expected:

        List<string> MyCollection = new List<string>()
        {
            "hello",
            "hello"
        };
        var tempCol = from i in MyCollection where i == "hello" select i;
        int count = tempCol.Count();

I doubt that it's the way you're calling it, either. The following works (returns false):

    static List<string> MyCollection = new List<string>()
        {
            "hello",
            "hello"
        };

    static bool ExistsUnique(Func<string, bool> p)
    {
        var tempCol = from i in MyCollection where p(i) select i;
        return tempCol.Count() == 1;
    }

    static void DoIt()
    {
        bool isUnique = ExistsUnique((s) => s.Equals("hello"));
        Console.WriteLine(isUnique);
    }

3 Comments

I think you may be right. Though the predicate my_set.ExistsUnique(s => s.Equals("Hello")) I think is fine. I am using the exact same in my Exists method(just checking if at least one element holds true, which works). Could it be the way I write it p(i)?
@PNS: See my updated answer. I don't think the problem is with the way you're calling it.
Now I'm truly confused, the only difference from your methods and my own is fact that your ExistsUnique method takes string,bool whereas mine takes T,bool but nonetheless it should still work. right?
1

Are you sure tempCol has looped completely through MyCollection? is Count() a method that forces the complete loop or is it lazy?

Does for example tempCol.ToList().Count give the correct result?

1 Comment

I tried saying p(i) || !p(i) where it added all the elements in the collection once (no duplicates), just to check if it actually iterated through all the elements which it did.
0

This implementation would make it so you don't have to actually enumerate the entire collection, so will save you some execution time.

public bool ExistsUnique(Func<T, bool> p)
{
    return MyCollection.Where(i => p(i)).Take(2).Count() == 1;
}

The Take(2) limits the Count to only enumerate the first two meeting the criteria.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.