简体   繁体   English

如何从通用日期列表中选择不同的(按唯一月份/年份)?

[英]How would I select distinct (by unique month/year) from a generic list of dates?

So the distinct is based on unique Month/Year, not just one distinct month (so I would want January of 2011 and January of 2012 to be distinct) 因此,独特的是基于独特的月/年,而不仅仅是一个不同的月(所以我希望2011年1月和2012年1月是截然不同的)

 // Test set of data
        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 = new List<DateTime>();

        /* need distinct list of DateTimes that are distinct by Month and Year and should be in UniqueMonthYears
         For example:
        new DateTime(2011, 1, 1)
        new DateTime(2011, 3, 1)
        new DateTime(2011, 5, 1)
        new DateTime(2012, 1, 1)
        new DateTime(2012, 2, 1)
        */
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));

You can create your own IEqualityComparer implementation and let Linq do the rest, like this: 您可以创建自己的IEqualityComparer实现,让Linq完成其余的工作,如下所示:

// 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();
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> . 会给你一个IEnumerable<DateTime>

Edit: David B's answer is better, but leaving this here as an option. 编辑:大卫B的答案更好,但将此作为选项。

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. 它给出的是能够设置string GetHash(Date)Date CreateFromHash(string)函数,从而使它更通用。

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);
        }

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;
    }
}

Aw! 噢! Everyone beat me to it. 每个人都打败了我。 And LINQ questions are so much fun. LINQ问题非常有趣。 ;-) ;-)

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. 我不知道你想如何从列表中选择唯一的日期,所以我没有假设您想要丢弃它,而是在子查询中使用了First方法。 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. 哦,还有奖励积分...如果你还没有,请从http://www.linqpad.net/下载Linqpad你可以很容易地尝试这种东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM