tags:

views:

154

answers:

4

I have a loop:

<% foreach (User usedBy in discountDto.UsedBy)
   { %>
     <%=usedBy.FullName%><br />
<% } %>

that often produces multiple lines with the same name:

Bob Smith
Mark Thomas
Mark Thomas
Steve Jones

I would like to aggregate the multiple lines to a single line followed by an integer representing the number of times that name occurred:

Bob Smith
Mark Thomas (2)
Steve Jones
+9  A: 

Please excuse formatting - wrong tools "to hand"...

foreach (User usedBy in discountDto.UsedBy.GroupBy(x => x.FullName))
{
    var count = usedBy.Count();
  %><%=usedBy.Key%><%
       if(count>1) %><%=" (" + count + ")"%><%
     %><br />
<% } %>
Marc Gravell
Not OP here but interested in answer.I get an invalid cast exception on the foreach line. "Unable to cast object of type 'Grouping[System.String,SolutionName.User]' to type 'SolutionName.User'." Where am I going wrong?
jammus
I believe the GroupBy is not going to return a user, but rather a IGrouping<String, User>. You just need to change the foreach (User usedBy in discountDto.UsedBy.GroupBy(x => x.FullName))toforeach (IGrouping<String, User> usedBy in discountDto.UsedBy.GroupBy(x => x.FullName))
Jace Rhea
Try changing the foreach line to: `foreach (var usedBy in discountDto.UsedBy.GroupBy(x => x.FullName))`
Charlino
That's great @jacerhea - thanks for your help
jammus
+1  A: 
var aggregatedUsers=from users in discountDto.UsedBy
                                    group user by user.FullName into result
                                    select new 
                                        {
                                            User=result.Key,
                                            Count= result.Count(),
                                        };
JasonTrue
You don't really need `into ... select` here. A LINQ query can terminate with `group ... by ...`, in which case the result is `IGrouping`, which would be mostly equivalent to your anonymous class here.
Pavel Minaev
That's true; come to think of it, the main reason why I used that excessive method is that most of the cases that I use group by in I have some other aggregate, such as LastLogin=result.Max(p=>p.LastLogin).
JasonTrue
Although come to think of it, using the anonymous type means that you'll only be calling the Count() method once; if you're always only displaying the count, you wouldn't need that small efficiency, but if you make a decision to display the count or not based on its value, as above, it would save you an extra iteration over the grouped elements.
JasonTrue
A: 

Try this: Please excuse the poor syntax... my Asp.Net is rusty, hope you get the idea...

  <% 
   Dictionary<string,int> counts = new Dictionary<string, int>();
   foreach (User usedBy in discountDto.UsedBy)   
   { %>
        <%if (counts.Contains(usedBy.FullName)) counts[usedBy.FullName]++; 
          else counts.Add(usedBy.FullName, 1);       
   } %>
   <% foreach(string usdBy in counts)
   { %> 
      <%=usdBy%><br /><% 
      <%if (counts[usdBy] > 1) 
        {%>
           (<%=counts[usdBy];%>) <%
        }%>
   } %>
Charles Bretana
You forgot to output the count in the second loop.
Pavel Minaev
Actually, second loop won't even compile, because you need a `KeyValuePair<string, int>` for the iteration variable.
Pavel Minaev
Not only is the second loop not using the right data type it's also attempting to output using a non-existent variable.
Joshua
thx, both of you, my ASP.Net is rusty
Charles Bretana
+1  A: 

Something like this worked for me

foreach (var item in list.GroupBy(u => new {u.Surname, u.FirstName}))
{
    %>
    <%=Html.Encode(item.Key.FirstName)%>
    <%=Html.Encode(item.Key.Surname)%>
    <%
    if (item.Count) > 1)
    {
        %>
        (<%=item.Count%>)
        <%
    }
}
jammus