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

PNS
  • 750
  • 1
  • 5
  • 19
  • I suspect [this SO question](http://stackoverflow.com/questions/2740109/can-someone-please-explain-this-lazy-evaluation-code) and its answers may help you. – ssamuel Nov 08 '11 at 00:34

4 Answers4

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;
}
shuniar
  • 2,592
  • 6
  • 31
  • 38
  • 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. – PNS Nov 08 '11 at 00:46
  • 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. – Paul Tyng Nov 08 '11 at 22:29
  • 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 Jul 19 '13 at 13:21
  • @shuniar I disagree but -1 removed. BTW when responding, people will know much much quicker if you put an at-theirname in your comment – Ruben Bartelink Jul 20 '13 at 08:42
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?

Peterdk
  • 15,625
  • 20
  • 101
  • 140
  • 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. – PNS Nov 08 '11 at 00:17
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);
    }
Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • 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 Nov 08 '11 at 00:27
  • @PNS: See my updated answer. I don't think the problem is with the way you're calling it. – Jim Mischel Nov 08 '11 at 00:33
  • 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? – PNS Nov 08 '11 at 00:44
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.

Paul Tyng
  • 7,924
  • 1
  • 33
  • 57