0

I am writing a class for an ASP.NET MVC menu. I want to be able to take a list of all blog entries and make an Archive menu similar to this:

1/2011 (2)
3/2011 (1)
4/2011 (12)
etc...

Here is the code that I am using:

public class ArchiveMenuModel
{
    private List<ArchiveMenuItem> menuItems;
    public List<ArchiveMenuItem> MenuItems { get { return menuItems; } }

    public ArchiveMenuModel(List<DateTime> dates, string streamUrl)
    {
        menuItems = new List<ArchiveMenuItem>();
        int itemCount = 0;
        dates = dates.OrderByDescending(x => x).ToList();

        for (int i=0; i<dates.Count; i++)
        {
            itemCount++;
            if(i+1 < dates.Count)
            {
                if(!(dates[i].Month == dates[i + 1].Month && dates[i].Year == dates[i + 1].Year))
                {
                    menuItems.Add(new ArchiveMenuItem(streamUrl, dates[i].Month, dates[i].Year, itemCount));
                    itemCount = 0;
                }
            }
            else
            {
                menuItems.Add(new ArchiveMenuItem(streamUrl, dates[i].Month, dates[i].Year, itemCount));
            }
        }

    }
}

Is there a better way, perhaps by using Linq or something? Specifically, the part of my code that i don't like is:

if(!(dates[i].Month == dates[i + 1].Month && dates[i].Year == dates[i + 1].Year))

If I can avoid such an ugly if statement, that would be great!

svick
  • 236,525
  • 50
  • 385
  • 514
quakkels
  • 11,676
  • 24
  • 92
  • 149
  • Is there LINQ in there? ;-) See `OrderBy` and `GroupBy` found in [IEnumerable](http://msdn.microsoft.com/en-us/library/system.linq.enumerable.aspx). The latter will get your groups (can then count elements in each group), the former will get the groups ordered. Decorate with the "fancy LINQ syntax" ... or leave as normal method calls (my preferred way). This isn't an answer because I'm just not gonna plop down any code -- play around in [LINQPad](http://linqpad.net) for a bit and enjoy! –  Jul 01 '11 at 20:41
  • When I was trying that, I had trouble getting the entry count. Also, is `GroupBy` able of returning `DateTime` groups by month+year and not time? – quakkels Jul 01 '11 at 20:45
  • Do you want to include empty months? I.e. lines like “5/2011 (0)”? – svick Jul 01 '11 at 20:53
  • @svick - I was thinking that I wouldn't include empty months. Can't come up with a good reason to... at least not yet ;-) – quakkels Jul 01 '11 at 21:00

5 Answers5

2
menuItems = dates
     .GroupBy(x => new DateTime(x.Year, x.Month, 1))
     .Select(x=> new{Date = x.Key, Count = x.Count()})
     .OrderByDescending(x => x.Date)
     .Select(x => new ArchiveMenuItem(streamUrl, 
                                      x.Date.Month, 
                                      x.Date.Year, 
                                      x.Count))
     .ToList();
Handcraftsman
  • 6,863
  • 2
  • 40
  • 33
1
var menuItems = from date in dates
                group date by new { date.Year, date.Month } into g
                select new { g.Key.Year, g.Key.Month, Count = g.Count() } into month
                orderby month.Year, month.Month
                select new ArchiveMenuItem(streamUrl, month.Month, month.Year, month.Count);

This LINQ query

  1. groups the dates by year and month
  2. selects the year, month and number of dates for each group
  3. orders the groups by year and month
  4. creates ArchiveMenuItem for each group
svick
  • 236,525
  • 50
  • 385
  • 514
0
var memuItems = dates.Select(o => o.Date.AddDays(-o.Date.Day + 1))
            .GroupBy(o => o, (o, p) => new {MonthAndYear = o.Date, EntriesCount = p.Count()})
            .Select(o => new ArchiveMenuItem(streamUrl, o.MonthAndYear.Month, o.MonthAndYear.Year, o.EntriesCount));

I subtract days to have date (to first of the month), so I can group using year and month.

MorioBoncz
  • 920
  • 11
  • 22
0

@svick and @danyolgiax,

Here's what I came up with from dany's link:

public class ArchiveMenuModel
{
    private List<ArchiveMenuItem> menuItems;
    public List<ArchiveMenuItem> MenuItems { get { return menuItems; } }

    public ArchiveMenuModel(List<DateTime> dates, string streamUrl)
    {
        menuItems = new List<ArchiveMenuItem>();
        dates = dates.OrderByDescending(x => x).ToList();

        var grouped = from d in dates
                        group d by new { month = d.Month, year= d.Year } into d     
                        select new { dt = d, count = d.Count() };
        foreach(var item in grouped)
        {
            menuItems.Add(new ArchiveMenuItem(streamUrl, item.dt.Key.month, item.dt.Key.year, item.count));
        }
    }
}
quakkels
  • 11,676
  • 24
  • 92
  • 149
-1

Something along these lines should work:

  menuItems.GroupBy(x => x.Month + "/" + x.Year);

I dont know the name of the property that holds the DateTime, but itd have to be set up like,

  menuItems.GroupBy(x => x.(datetime).Month + "/" + x.(datetime).Year); 
Sean Thoman
  • 7,429
  • 6
  • 56
  • 103