C# List - Group By - Without Linq
Solution 1
Easiest answer: use LINQBridge and get all your LINQ to Objects goodness against .NET 2.0... works best if you can use C# 3 (i.e. VS2008 but targeting .NET 2.0).
If you really can't do that, you'll basically need to keep a dictionary from a key to a list of values. Iterate through the sequence, and check whether it already contains a list - if not, add one. Then add to whatever list you've found (whether new or old).
If you need to return the groups in key order, you'll need to also keep a list of keys in the order in which you found them. Frankly it's a pain... just get LINQBridge instead :)
(Seriously, each individual bit of LINQ is actually fairly easy to write - but it's also quite easy to make off-by-one errors, or end up forgetting to optimize something like Count()
in the case where it's actually an ICollection<T>
... There's no need to reinvent the wheel here.)
EDIT: I was about to write some code, but then I noticed that you want a list returned... a list of what? A List<IList<IObject>>
? Or are you actually trying to group and sum in one go? If so, don't you want a list of pairs of key and amount? Or are you going to reuse the same class that you've already got for a single account, but as the aggregate? If it's the latter, here's some sample code:
public static IList<IObject> SumAccounts(IEnumerable<IObject> data)
{
List<IObject> ret = new List<IObject>();
Dictionary<string, IObject> map = new Dictionary<string, IObject>();
foreach (var item in data)
{
IObject existing;
if (!map.TryGetValue(item.Account, out existing))
{
existing = new IObject(item.Account, 0m);
map[item.Account] = existing;
ret.Add(existing);
}
existing.Amount += item.Amount;
}
return ret;
}
Admittedly the extra efficiency here due to using a Dictionary
for lookups will be pointless unless you've got really quite a lot of accounts...
EDIT: If you've got a small number of accounts as per your comment, you could use:
public static IList<IObject> SumAccounts(IEnumerable<IObject> data)
{
List<IObject> ret = new List<IObject>();
foreach (var item in data)
{
IObject existing = ret.Find(x => x.Account == item.Account);
if (existing == null)
{
existing = new IObject(item.Account, 0m);
ret.Add(existing);
}
existing.Amount += item.Amount;
}
return ret;
}
Solution 2
Use a dictionary to hold the results. Locating an item in a dictionary is close to an O(1) operation, so it's a lot faster than searching for items in a list.
Dictionary<string, decimal> sum = new Dictionary<string, decimal>();
foreach (IObject obj in objects) {
if (sum.ContainsKey(obj.Account)) {
sum[obj.Account].Amount += obj.Amount;
} else {
sum.Add(obj.Account, obj.Amount);
}
}
Martin
Software Architect and Team Lead specializing in JS, Node, and .Net.
Updated on June 15, 2022Comments
-
Martin almost 2 years
I have an object:
IObject { string Account, decimal Amount }
How do I group by Account and Sum the Amount, returning a List without Linq.
2.0 Framework ... that is why no Linq.
Here is what I have:
ListofObjects = List<IObject>; foreach (var object in objects) { var objectToAdd = new Object(object); var oa = ListofObjects.Find(x => x.Account == objectToAdd.Account); if (oa == null) { ListofObjects.Add(objectToAdd); } else { ListofObjects.Remove(oa); oa.Amount = objectToAdd.Amount; ListofObjects.Add(oa); } }