score:9

Accepted answer
List<DateTime> result = source
  .Select(d => new DateTime(d.Year, d.Month, 1))
  .Distinct()
  .ToList();

Also useful:

ILookup<DateTime, Order> ordersByMonth = ordersSource
  .ToLookup(order => new DateTime(order.OrderDate.Year, order.OrderDate.Month, 1));

score:1

Here's the shortest code I'd know of:

    List<DateTime> UniqueMonthYears = 
        CompleteListOfDates.Select(t => new DateTime(t.Year, t.Month, 1))
            .Distinct()
            .ToList();

Here's more structural but way less performant approach (for demo purpose only). What it give is the ability to set string GetHash(Date) and Date CreateFromHash(string) functions, thus making it more generic.

Code:

private void Form1_Load(object sender, EventArgs e)
        {

            List<DateTime> CompleteListOfDates = new List<DateTime>();

            CompleteListOfDates.Add(new DateTime(2011, 1, 1));
            CompleteListOfDates.Add(new DateTime(2011, 1, 5));
            CompleteListOfDates.Add(new DateTime(2011, 3, 1));
            CompleteListOfDates.Add(new DateTime(2011, 5, 1));
            CompleteListOfDates.Add(new DateTime(2011, 5, 1));
            CompleteListOfDates.Add(new DateTime(2012, 1, 1));
            CompleteListOfDates.Add(new DateTime(2012, 2, 1));

            List<DateTime> UniqueMonthYears = 
                CompleteListOfDates.Select(t => 
                    GetDateHash(t)).Distinct().Select(t => 
                        CreateFromDateHash(t)).ToList();


            MessageBox.Show(UniqueMonthYears.Count.ToString());
        }

        private static string GetDateHash(DateTime date)
        {
            return date.ToString("MMM-yyyy");
        }

        private static DateTime CreateFromDateHash(string hash)
        {
            return DateTime.Parse("1-" + hash);
        }

score:2

var uniqueMonthYears = CompleteListOfDates
                          .GroupBy(d => new{ d.Month, d.Year})
                          .Select(my => new DateTime(my.Key.Year, my.Key.Month, 1));

Will give you an IEnumerable<DateTime>.

Edit: David B's answer is better, but leaving this here as an option.

score:4

You can create your own IEqualityComparer implementation and let Linq do the rest, like this:

// implement this
public class DateTimeComparer : IEqualityComparer<DateTime>
{
    public bool Equals(DateTime x, DateTime y)
    {
        return x.Year == y.Year && x.Month == y.Month;
    }

    public int GetHashCode(DateTime obj)
    {
        return obj.Year * 100 + obj.Month;
    }
}

// use it like this
UniqueMonthYears = CompleteListOfDates.Distinct(new DateTimeComparer()).ToList();

score:1

How about somthing like

completeListOfDates.Select(d => new {Year = d.Year, Month = d.Month}).Distinct()

Preserving the first occuring day part is essentially meaningless but you could do so like this.

completeListOfDates.Distinct(new YearMonthComparer());

private class YearMonthComparer : IEqualityComparer<DateTime>
{
    public bool Equals(DateTime x, DateTime y)
    {
        if(x.Year != y.Year) return false;
        if(x.Month != y.Month) return false;
        return true;
    }

    public int GetHashCode(DateTime obj)
    {
        int hash = 13;
        hash = (hash * 7) + obj.Year.GetHashCode();
        hash = (hash * 7) + obj.Month.GetHashCode();
        reutrn hash;
    }
}

score:0

Aw! Everyone beat me to it. And LINQ questions are so much fun. ;-)

I didn't know how you wanted to select the unique date from the list, so instead of assuming you wanted to discard that, I used the First method in a subquery. Obviously you could change that using a sort of some kind as well as the predicate.

How about this then?

var UniqueMonthYears = (from date in CompleteListOfDates
                        let month = date.Month
                        let year = date.Year
                        let day = (CompleteListOfDates.First(d => d.Month == month && d.Year == year))
                        select day).Distinct();

Oh, and for bonus points... if you've not already, download Linqpad from http://www.linqpad.net/ You can try out this kind of stuff really easily with that.


Related Articles