Given the following class:
public class Transaction
{
public string Category { get; set; }
public string Form { get; set; }
}
How do I get a grouping of transactions that are grouped by both the Category
and the Form
?
Basically I want it to output like so:
Category 1
Form 1
Transaction1
Transaction2
Transaction3
...
Form 2
Transaction1
Transaction2
Transaction3
...
Category 2
Form 1
Transaction1
Transaction2
Transaction3
...
Form 2
Transaction1
Transaction2
Transaction3
...
C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...
What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.
Compared to other languages—like Java, PHP, or C#—C is a relatively simple language to learn for anyone just starting to learn computer programming because of its limited number of keywords.
History: The name C is derived from an earlier programming language called BCPL (Basic Combined Programming Language). BCPL had another language based on it called B: the first letter in BCPL.
Basically, the first group should contain every piece of information that you will need to group by later, and then on repeat groupings, remove one piece of information that is relevant for that grouping level. Do this as many times as you need.
ValueTuple
can help a lot, since it lets you have a composite key that can be passed to another type. Otherwise with anonymous types, you'll need to rely on type inference to pass the groups to something else. One issue with ValueTuple
though is that you can't have a 1-Tuple for some reason, so in that case you need to group by the single property and not use a tuple.
If you already have a hierarchical relationship in your data structure, then grouping by a Tuple
might be unnecessary.
var groups =
transactions
.GroupBy(tran => (
Category: tran.Category,
Form: tran.Form
)).GroupBy(group => group.Key.Form)
.ToList();
The type gets complicated very fast, so use type inference and refactoring tools to avoid having to figure out the specific type, when possible. For example, just the above results in the type:
List<IGrouping<string, IGrouping<(string Category, string Form), Transaction>>>
I ended up with the following, because the grouping need to be complete before the iteration over the collection.
Seed Some Transactions
var cats = new[] { "Category 1", "Category 2", "Category 3" };
var frms = new[] { "Form 1", "Form 2", "Form 3" };
var transactions = new List<Transaction>();
for (var i = 0; i <= 150; i++)
{
transactions.Add(new Transaction
{
Category = i % 2 == 0 ? cats[0] : i % 3 == 0 ? cats[1] : cats[2],
Form = i % 5 == 0 ? frms[0] : i % 7 == 0 ? frms[1] : frms[2]
});
}
The Grouping
var groupedTransactions = transactions.GroupBy(x => x.Category)
.Select(x => new
{
Category = x.Key,
Forms = x.ToList()
.GroupBy(y => y.Form)
});
Write it to the Console
foreach (var group in groupedTransactions.OrderBy(x => x.Category))
{
Console.WriteLine(group.Category);
foreach (var form in group.Forms.OrderBy(x => x.Key))
{
Console.WriteLine("\t" + form.Key);
foreach (var transaction in form)
{
Console.WriteLine("\t\t" + transaction.Id);
}
}
}
Here is an example using nested foreach loops, I'm not sure how you would do this in a single string of linq statements, maybe with lots of selectmanys?
var transactions = new[]{
new{Category = "1", Form = "1", Title = "Trans1" },
new{Category = "1", Form = "1", Title = "Trans2" },
new{Category = "1", Form = "1", Title = "Trans3" },
new{Category = "1", Form = "2", Title = "Trans1" },
new{Category = "1", Form = "2", Title = "Trans2" },
new{Category = "1", Form = "2", Title = "Trans3" },
new{Category = "2", Form = "1", Title = "Trans1" },
new{Category = "2", Form = "1", Title = "Trans2" },
new{Category = "2", Form = "1", Title = "Trans3" },
new{Category = "1", Form = "3", Title = "Trans1" },
new{Category = "1", Form = "3", Title = "Trans2" },
new{Category = "1", Form = "3", Title = "Trans3" },
};
foreach(var byCategory in transactions.GroupBy(x => x.Category))
{
Console.WriteLine(byCategory.Key);
foreach(var byForm in byCategory.GroupBy(x => x.Form))
{
Console.WriteLine("\t" + byForm.Key);
foreach(var trans in byForm)
{
Console.WriteLine("\t\t" + trans.Title);
}
}
}
Just because I was curious what it would look like I came up with the following, YOU SHOULD NOT USE THIS IN PRODUCTION CODE as it is ridiculous (if you do have a data structure like this it should be broken up into something like Dictionary<CategoryName, FormGroup>
or something with meaningful types)
Dictionary<string, Dictionary<string, List<string>>> tooManyDictionaries = transactions
.GroupBy(x => x.Category)
.ToDictionary(
catGroup => catGroup.Key,
catGroup => catGroup
.GroupBy(x => x.Form)
.ToDictionary(
formGroup => formGroup.Key,
formGroup => formGroup.Select(x => x.Title).ToList()));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With