The outcome you want can be achieved using System.Linq.GroupBy for the logic and DataGridView (Winforms Example) for the views:

The amount of code to write is small! Just use Data Binding Winforms, Xamarin, WPF for two lists:
First make two classes containing the properties you wish to display:
internal class UserPurchase
{
public string Name { get; internal set; } // Internal makes item ReadOnly in view
public string Purchase { get; internal set; }
}
and
internal class PurchasedItem
{
public string Description { get; internal set; }
public int Count { get; internal set; }
}
Then declare two BindingList (Winforms example) or ObservableCollection (others).
internal BindingList<UserPurchase> PurchasesByUser { get; } = new BindingList<UserPurchase>();
internal BindingList<PurchasedItem> PurchasedByItem { get; } = new BindingList<PurchasedItem>();
This shows how to attach the bindings in Winforms (for other platforms use the xaml). As a result DataGridView will now generate columns automatically using the properties of the two classes.
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if (!DesignMode)
{
// Bind the lists to the UI element to display them.
dataGridView1.DataSource = PurchasesByUser;
dataGridView2.DataSource = PurchasedByItem;
initList1();
}
}
Here is the list initializer which populates List1 PurchasesByUser then applies System.Linq.GroupBy on List1 to calculate List2 PurchasedByItem:
private void initList1()
{
XElement xsource = XElement.Parse(source);
XElement[] xusers = xsource.Elements("user").ToArray();
foreach (XElement xuser in xusers)
{
XElement[] xpurchases = xuser.Element("items").Elements("item").ToArray();
foreach (XElement xpurchase in xpurchases)
{
var xname = xuser.Element("name");
// Every time you add a record to the list, it appears in the View automatically.
PurchasesByUser.Add(new UserPurchase { Name = (string)xname, Purchase = (string)xpurchase });
}
}
// Make what is essentially a list *of* lists using System.Linq.GroupBy
var groups =
PurchasesByUser.GroupBy(purchase => purchase.Purchase);
foreach (var group in groups)
{
// Decoding the Group:
Debug.WriteLine(
$"The group '{group.Key}' holds {group.Count()} items");
Debug.WriteLine(
$"The customers are { string.Join(",", group.ToList().Select(item=>item.Name))}");
var byItem = new PurchasedItem { Description = group.Key, Count = group.Count() };
PurchasedByItem.Add(byItem);
}
}
Finally to model your database (with subitems), I used this string in Xml format:
const string source =
@"<purchases>
<user>
<name>John Doe</name>
<items>
<item>Shoes</item>
<item>T-Shirts</item>
<item>Jeans</item>
</items>
</user>
<user>
<name>Rick Astley</name>
<items>
<item>Baseball bat</item>
<item>Sandwich</item>
<item>T-Shirts</item>
<item>Shoes</item>
</items>
</user>
<user>
<name>Jane Doe</name>
<items>
<item>Shoes</item>
<item>T-Shirts</item>
</items>
</user>
<user>
<name>Anonymous</name>
<items>
<item>Laptop</item>
</items>
</user>
<user>
<name>Joe Mm.</name>
<items>
<item>Milk</item>
<item>Shoes</item>
</items>
</user>
</purchases>";
This is just one approach of course but your question is a good way to demonstrate combining concepts from System.Linq System.Xml.Linq and Data Binding. I posted my code on GitHub if you'd like to experiment with it.