0

I have a query, which is defined like this, and I used it to generate a JSON output. The point is that, I have applied Distinct method to it, but it still shows duplicate items. For example, I have many items with value "Unclassifed", whereas I want only one of it, same goes for some other values. Here is my query:

var results = db.Users.OfType<Business>()
              .Where(b => b.StateID == state && (term == null || b.Description.ToLower().Contains(term.ToLower())))
              .Distinct().Select(x => new { id = x.StateID, value = x.Description }).Take(5).ToList();

Any idea, how to fix it? I guess I need to specifically apply Distinct to the value somehow.

1
  • What field is "Unclassified" in? If two records have the same value in that field, which do you take? Commented Sep 11, 2014 at 13:24

5 Answers 5

4

I suspect you need to switch your Distinct and Select calls. Distinct will compare more fields than you are probably expecting given your projection, which may mean fields other than the ones you actually want to compare on are being compared. Calling Select first will reduce the number of fields that are compared to generate the distinct list.

i.e.

var results = db.Users.OfType<Business>()
          .Where(b => b.StateID == state && (term == null ||  b.Description.ToLower().Contains(term.ToLower())))
          .Select(x => new { id = x.StateID, value = x.Description })
          .Distinct()
          .Take(5)
          .ToList();
Sign up to request clarification or add additional context in comments.

9 Comments

Not sure why the downvote, looks like a reasonable answer to me
"Distinct will compare all fields in your class" Only for anonymous types and types that have overridden Equals to compare all fields. I didn't downvote but wanted to make that point clear.
+1 for originality, but agreed with @DStanley, its better to edit answer to avoid misunderstanding
Thanks for the corrections. I have updated my answer. Glad the OP found it useful.
@DanielKelley, I think you correct it in a wrong way :). Field by field comparison as default behavior is just for anonymous types, not for custom classes, like Business. Probably you even don't understand why your answer works :).
|
1

.NET has no way of knowing how you want to determine "equality" in your objects. By default, equality of reference types is based only on reference equality, so all of your objects are already distinct by default.

You can provide a custom equality comparer to Distinct(). For example, if you're just comparing on a .Name property to determine uniqueness, it might look something like this:

class BusinessComparer : IEqualityComparer<Business>
{
    public bool Equals(Business x, Business y)
    {
        if (Object.ReferenceEquals(x, y))
            return true;
        if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
            return false;
        return x.Name == y.Name;
    }

    public int GetHashCode(Business business)
    {
        if (Object.ReferenceEquals(business, null))
            return 0;
        int hashBusinessName = business.Name == null ? 0 : business.Name.GetHashCode();
        return hashProductName;
    }
}

If this equality is core business logic and not just used in this particular comparison then you might even implement Equals and GetHashCode on Business itself so that equality comparison can be used elsewhere. Just be aware that could be a breaking change to existing code which already assumes referential equality.

Comments

0

Business class needs to override object.Equals and object.GetHashCode methods and implemented IEquatable<T> before the Distinct method will function correctly.

See MSDN examples: Enumerable.Distinct Method (IEnumerable)

3 Comments

That's only one way - you could also implement an IEqualityComparer<Business>, which may be a better choice since overrriding Equals would apply to all scenarios rather than just this one.
It's Microsoft's recommended method for providing a default IEqualityComparer actually. An IEqualityComparer<Business> would be great if you needed to run a different Distinct() from the Default one. It's all there in the notes under the link. :)
Yes but the requirement in the question may not necessarily need to be the "default" for that type. It may just be for this scenario, in which case a non-default IEqualityComparer would be more appropriate.
0

Distinct() returns distinct elements from a sequence by using the default equality comparer to compare values. so you should create BusinessEqualityComparer class should Implement IEqualityComparer Interface

class BusinessEqualityComparer : IEqualityComparer<Business>
{

    public bool Equals(Business b1, Business b2)
    {
        if (b1.ID == b2.ID)
        {
            return true;
        }
        else
        {
            return false;
        }
    }


    public int GetHashCode(Business business)
    {
        int hCode = business.ID ^ business.ID ^ business.ID;
        return hCode.GetHashCode();
    }

Comments

0

.Distinct() without providing a manual comparer will use the default comparer of the processed type which will ultimately use the .Equals() and .GetHashCode() of your Business class.

So unless you have overridden those methods .Distinct() will only remove reference-wise duplicates.

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.