Rajat Suneja
Rajat Suneja

Reputation: 552

Count Occurrences of an Item in a List using LINQ

I am trying to calculate the occurrences of an Item in a list using LINQ,

I have the following schema -

User (All Entries Provided), Count (To be Calculated)

The count should be like -

enter image description here

I cannot think of an elegant solution to this.

Is it even possible only using LINQ? If not, how can we achieve this using LINQ with some C# Code.

Upvotes: 11

Views: 16402

Answers (4)

Ian Mercer
Ian Mercer

Reputation: 39277

You can do it without double iterating the set like this:-

        var values = "ABAABCA".ToArray();

        var counts = new Dictionary<char, int>();

        var counter = values.Select(ch => 
            { 
                int count = counts[ch] = counts.ContainsKey(ch) ? counts[ch] + 1 : 1;
                return new {ch, count}; 
            });

        foreach (var c in counter)
            Console.WriteLine(c.ch + " -> " + c.count);

Upvotes: 1

user2500532
user2500532

Reputation: 1

I did this by using Select. I was writing a sync function that got items from the World of Warcraft API and certain items weren't on the API anymore, even though they technically were still in the game. Since I was syncing items in order of number of times they were listed on the auction house, it would get stuck on these certain items.

I created a list of bad item IDs. If the item wouldn't sync, I added it to the list. I wanted to skip the item if it couldn't sync 3 times. I accomplished that with the following code:

private int[] GetBadItemList()
{
    var ids = from i in _badItemIDs
              where _badItemIDs.Select(curItem => curItem.Equals(i)).Count() >= MAX_ITEM_TRYS
              select i;
    return ids.ToArray();
}

Upvotes: 0

Habib
Habib

Reputation: 223237

You can do that with a combination of loop and Enumerable.Take, Something like:

for (int i = 0; i < list.Count; i++)
{
    //Get count of current element to before:
    int count = list.Take(i+1)
                    .Count(r => r.UserName == list[i].UserName);
    list[i].Count = count;
}

Where your list is defined as:

List<User> list = new List<User>
    {
        new User{UserName = "A"},
        new User{UserName = "B"},
        new User{UserName = "A"},
        new User{UserName = "A"},
        new User{UserName = "B"},
        new User{UserName = "A"},
        new User{UserName = "C"},
        new User{UserName = "A"},

    };

and User class as:

public class User
{
    public string UserName { get; set; }
    public int Count { get; set; }
}

Later you can print the output like:

foreach (var item in list)
{
    Console.WriteLine("UserName: {0}, Running Total: {1}", item.UserName, item.Count);
}

and you will get:

UserName: A, Running Total: 1
UserName: B, Running Total: 1
UserName: A, Running Total: 2
UserName: A, Running Total: 3
UserName: B, Running Total: 2
UserName: A, Running Total: 4
UserName: C, Running Total: 1
UserName: A, Running Total: 5

Upvotes: 3

Justin Niessner
Justin Niessner

Reputation: 245419

Can it be done with LINQ? Probably not easily. Can it be done with your own extension method fairly easily? Sure (I haven't actually tried to compile and run the code so I can't guarantee that it'll work, but it's definitely a good starting point):

public static IEnumerable<Tuple<T, int>> RunningTotal<T>(this IEnumerable<T> source)
{
    var counter = new Dictionary<T, int>();

    foreach(var s in source)
    {
        if(counter.ContainsKey(s))
        {
            counter[s]++;
        }
        else
        {
            counter.Add(s, 1);
        }

        yield return Tuple.Create(s, counter[s]);
    }
}

Upvotes: 3

Related Questions