DenaliHardtail
DenaliHardtail

Reputation: 28306

How do I perform a DISTINCT query using LINQ (.NET 4)?

I have an objectset and want to perform a DISTINCT on the collection. The objects returned have about 10 properties each. I want to use the properties so projecting really isn't an option. Of the 10 properties, I only want the DISTINCT applied to two of the fields (DistrictId and ContactId). How do I do this?

Upvotes: 2

Views: 1552

Answers (4)

Tore Aurstad
Tore Aurstad

Reputation: 3806

We can define an extension method to do a DistinctBy operation on an IEnumerable of T like the following:

public static class EnumerableExtensions
        {

            /// <summary>
            /// Returns a ienumerable which is distinct by a given property key selector. If a custom equality 
            /// comparer is to be used, pass this in as the comparer. By setting the comparer default to null,
            /// the default comparer is used. 
            /// </summary>
            /// <typeparam name="T">The item type in the ienumerable</typeparam>
            /// <typeparam name="TKey">The type of the key selector (property to disinct elements by)</typeparam>
            /// <param name="coll">The source ienumerable</param>
            /// <param name="keySelector">The key selector, use a member expression in a lambda expression</param>
            /// <param name="comparer">Custom comparer to use, pass in null here to specify that default comparer is used,
            /// however, this is default set to null and not required parameter</param>
            /// <returns></returns>
            public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> coll, Func<T, TKey> keySelector,
                IEqualityComparer<TKey> comparer = null)
            {
                if (coll == null)
                    throw new ArgumentNullException("coll");
                if (keySelector == null)
                    throw new ArgumentNullException("keySelector");

                var result = coll.GroupBy(keySelector, comparer).Select(g => g.First()).ToList();
                return new List<T>(result).AsEnumerable();
            }

        }

The DistinctBy operator can then be tested for example in a simple Console application, where we want to get distinct (Model, Color) tuples i.e. we want only one car of the combination Model+Color:

class Program {

    static void Main(string[] args)
    {

        var cars = new []
        {
            new Car {Model = "Audi", Make = "A4", Color = "Black"},
            new Car {Model = "Audi", Make = "A8", Color = "Red"},
            new Car {Model = "Audi", Make = "TT", Color = "Black"},
            new Car {Model = "Volvo", Make = "XC90", Color = "Black"},
            new Car {Model = "Volvo", Make = "S90", Color = "Black"},
            new Car {Model = "Ferrari", Make = "F500", Color = "Yellow"},
            new Car {Model = "Ferrari", Make = "F500", Color = "Red"},
            new Car {Model = "Lada", Make = "Limousine", Color = "Rusty"}
        };

        var groupedCars = cars.DistinctBy(c => new {c.Model, c.Color});


        foreach (var gc in groupedCars)
        {
            Console.WriteLine(gc.ToString()); 
        }

        Console.WriteLine("Press any key to continue ...");
        Console.ReadKey(); 
    }




    // Define other methods and classes here

}

The output is then:

Model: Audi, Make: A4, Color: Black
Model: Audi, Make: A8, Color: Red
Model: Volvo, Make: XC90, Color: Black
Model: Ferrari, Make: F500, Color: Yellow
Model: Ferrari, Make: F500, Color: Red
Model: Lada, Make: Limousine, Color: Rusty
Press any key to continue ...

We did not get the item "Audi TT Black" because we already got a black Audi. We did not get the "Volvo S90 Black" because we already got a black Volvo. We got both the Ferrari F500 because they got different colors. And sadly, we are stuck with the "Lada Limousine Rusty" since it was the only combination of Model and Color.

Upvotes: 0

matgerva
matgerva

Reputation: 72

You can try construct an anonymous type for handle this case. Suppose your class with 10 properties is Element

public class Element
    {
        public int FirstProp { get; set; }
        public int SecondProp { get; set; }

       //others 8 cool properties
    }

The query for extracting what you want with an extension method in linq:

IList<Element> element = new List<Element>();


            var result = new { P1 = element
                                .Select(X => X.FirstProp).Distinct()
                            ,
                               P2 = element
                                    .Select(X => X.SecondProp).Distinct()
                            ,
                                element
                               // do projections here over others 8 properties


            };

Upvotes: 0

BrokenGlass
BrokenGlass

Reputation: 160852

Since you want only elements that are different in terms of the combination of DistrictId and ContactId you could use GroupBy, then decide how you want to treat the duplicates. Each group in this case represents the items that fall into one distinct combination.

var results = context.MyCollection
                     .GroupBy( x=> new { x.DistrictId, x.ContactId })
                     .Select(...)

Upvotes: 4

Jeff Yates
Jeff Yates

Reputation: 62377

You need to write your own or obtain an implementation of a DistinctBy method that allows you to perform distinction on projected information of the sequence contents while preserving the sequence type.

MoreLINQ provides an implementation of this method.

Upvotes: 1

Related Questions