1

I'm creating a software for a marketplace and want to create a listview that counts all the duplicate values inside a column.

This count will show how many items were oftenly bought by buyers.

This is the concept of the ListView should be. It counts from column of ListView1, and put the duplicates, count it, and display the data inside of ListView2.

Concept

I need to know:

  • How to Count all the duplicated value inside the "Purchased Items" column.
  • And display the count result inside another ListView with the value itself.

Note: In this case, the value inside the "Name" is an Item and the "Purchased Items" is a Subitem. Those data were from simply typing in a textbox and clicking on a button.

3
  • Anyway, if you think this is a duplicate & there's an exact answer to that. Please provide me a link to it, huge thanks!. Commented May 27, 2022 at 13:29
  • Where are you getting this values from, a database? Commented May 27, 2022 at 13:30
  • No, just by manualling adding it via a textbox & button. (it'll be way more harder for me to do that lol) Commented May 27, 2022 at 13:43

4 Answers 4

1

Group the items of the input ListView by the values of the second ColumnHeader (index = 1) to get IEnumerable<IGrouping<string, ListViewItem>>, then use the elements (of type IGrouping<string, ListViewItem>) Key property and Count method to create the ListViewItem objects of the output ListView.

Here's a one liner:

listView2.Items.AddRange(listView1.Items
    .Cast<ListViewItem>()
    .GroupBy(x => x.SubItems[1].Text, StringComparer.OrdinalIgnoreCase)
    .Select(x => new ListViewItem(new[] { x.Key, x.Count().ToString() }))
    .ToArray());
Sign up to request clarification or add additional context in comments.

Comments

0

So if I understand you want to have another listview with duplicates and the count?

If so you can create a List<> and then add that item into the list in this case will be a string I believe, maybe something like this? This is just a console app for demonstration.

List<string> myList = new List<string>();

myList.Add("One");
myList.Add("Two");
myList.Add("Three");
myList.Add("One");
myList.Add("Two");
myList.Add("Four");

List<string> noDupes = myList.Distinct().ToList();


foreach(string dupe in noDupes)
{
    Console.WriteLine(dupe);
}

Console.WriteLine(noDupes.Count());

1 Comment

This shows non dupes, so you can then do opposite for dupes to a new list
0

Given the next list:

List<myClass> items = new List<myClass>()
{
    new myClass() { Name = "John Doe", Item = "Shoes" },
    new myClass() { Name = "John Doe", Item = "T-Shirt" },
    new myClass() { Name = "John Doe", Item = "Jeans" },
    new myClass() { Name = "Rick Astley", Item = "Baseball bat" },
    new myClass() { Name = "Rick Astley", Item = "Sandwich" },
    new myClass() { Name = "Rick Astley", Item = "T-Shirt" },
    new myClass() { Name = "Rick Astley", Item = "Shoes" },
    new myClass() { Name = "Jane Doe", Item = "Shoes" },
    new myClass() { Name = "Jane Doe", Item = "T-Shirt" },
    new myClass() { Name = "Joe Mm.", Item = "Shoes" },
    new myClass() { Name = "Joe Mm.", Item = "Milk" },
};

You can group and count in this way:

var l = items.GroupBy(group => group.Item)
                .Select(n => new { Item = n.Key, Count = n.Count() })
                .ToList();

The result:

Item: Shoes  Count: 4
Item: T-Shirt  Count: 3
Item: Jeans  Count: 1
Item: Baseball bat  Count: 1
Item: Sandwich  Count: 1
Item: Milk  Count: 1

Comments

0

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

enter image description here

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.