0

This is my linq query and I get lots of duplicates with school names. so I created a regex function to trim the text:

public static string MyTrimmings(string str)
        {
        return Regex.Replace(str, @"^\s*$\n", string.Empty, RegexOptions.Multiline).TrimEnd();
        }

the text gets trimed alright, however, the dropdown values are all duplicates! please help me eliminate duplicates, oh Linq joy!!

ViewBag.schools = new[]{new SelectListItem
            {
                Value = "",
                Text = "All"
            }}.Concat(
            db.Schools.Where(x => (x.name != null)).OrderBy(o => o.name).ToList().Select(s => new SelectListItem
            {
                Value = MyTrimmings(s.name),
                Text = MyTrimmings(s.name)
            }).Distinct()
            );    
Servy
  • 202,030
  • 26
  • 332
  • 449
NULL
  • 1,559
  • 5
  • 34
  • 60
  • i didn't know distinct take values good to know. thanks i will try that now. – NULL Oct 03 '13 at 19:01
  • it said cannot compare lambda expression to iequalitycomparer error – NULL Oct 03 '13 at 19:02
  • 1
    See [morelinq's DistinctBy](https://code.google.com/p/morelinq/source/browse/MoreLinq/DistinctBy.cs?r=d4396b9ff63932be0ab07c36452a481d20f96307) – L.B Oct 03 '13 at 19:12
  • Do your `SelectListItem` implement `IComparable`? If not you should, because `Distinct` uses this interface to do the comparaison. If it's not present, it uses the one from `object` – Shimrod Oct 03 '13 at 19:14
  • @Shimrod no it doesn't how do i do that? – NULL Oct 03 '13 at 19:17
  • I meant `IEquatable`, not `IComparable`, see my answer. – Shimrod Oct 03 '13 at 19:22

3 Answers3

4

Distinct is poor, GroupBy for the win:

db.Schools.GroupBy(school => school.name).Select(grp => grp.First());
weston
  • 54,145
  • 21
  • 145
  • 203
0

Assuming you have a School class you can write an IEqualityComparer

class SchoolComparer : IEqualityComparer<School>
{
    public bool Equals(School x, School y)
    {
        //Check whether the compared objects reference the same data. 
        if (Object.ReferenceEquals(x, y)) return true;

        //Check whether any of the compared objects is null. 
        if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
            return false;

        //Check whether the school' properties are equal. 
        return x.Name == y.Name;
    }

    // If Equals() returns true for a pair of objects  
    // then GetHashCode() must return the same value for these objects. 

    public int GetHashCode(School school)
    {
        //Check whether the object is null 
        if (Object.ReferenceEquals(school, null)) return 0;

        //Get hash code for the Name field if it is not null. 
        int hashSchoolName = school.Name == null ? 0 : school.Name.GetHashCode();

        //Calculate the hash code for the school. 
        return hashSchoolName;
    }
}

Then your linq query would look like this:

db.Schools.Where(x => x.name != null)
          .OrderBy(o => o.name).ToList()
          .Distinct(new SchoolComparer())
          .Select(s => new SelectListItem
            {
                Value = MyTrimmings(s.name),
                Text = MyTrimmings(s.name)
            });  
Esteban Elverdin
  • 3,552
  • 1
  • 17
  • 21
  • i get his error: LINQ to Entities does not recognize the method 'System.Linq.IQueryable`1[namespace.Models.School] Distinct[School](System.Linq.IQueryable`1[namespace.Models.School], System.Collections.Generic.IEqualityComparer`1[namespace.Models.School])' method, and this method cannot be translated into a store expression. – NULL Oct 03 '13 at 19:29
  • I updated the answer, I had put the Distinct in the wrong place – Esteban Elverdin Oct 03 '13 at 19:31
  • @EstebanElverdin Bringing in the entire result set and doing the work on the client side adds a *lot* of cost over doing the work on the DB end, which can both do it faster (usually) and reduce network traffic. Additionally, in the even that you *do* want to have a query run a LINQ to objects method you should use `AsEnumerable` not `ToList` to avoid eagerly executing the query, forcing the entire query to be in memory rather than having it streamed, and needlessly creating and then discarding a `List` object. – Servy Oct 03 '13 at 19:39
  • @Servy, I understand your point, I was just trying to make the code in the question works as expected. Regarding the ToList vs AsEnumerable, in this particular case I think there is no difference. – Esteban Elverdin Oct 03 '13 at 19:45
  • @EstebanElverdin I just listed three differences between them, all of which apply to this example. – Servy Oct 03 '13 at 20:02
0

You could make your class implement the IEquatable<T> interface, so Distinct will know how to compare them. Like this (basic example):

public class SelectListItem : IEquatable<SelectListItem>
{
    public string Value { get; set; }
    public string Text { get; set; }

    public bool Equals(SelectListItem other)
    {
        if (other == null)
        {
            return false;
        }

        return Value == other.Value && Text == other.Text;
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int hash = 17;

            if (Value != null)
            {
                hash = hash * 23 + Value.GetHashCode();
            }

            if (Text != null)
            {
                hash = hash * 23 + Text.GetHashCode();
            }

            return hash;
        }
    }
}

(GetHashCode taken fron John Skeet's answer here: https://stackoverflow.com/a/263416/249000)

Community
  • 1
  • 1
Shimrod
  • 3,115
  • 2
  • 36
  • 56