Подсчет вхождений элемента в списке с помощью LINQ

Я пытаюсь вычислить вхождения элемента в список с помощью LINQ,

У меня есть следующие схемы

пользователь (все записи при условии), количество (для расчета)

граф должен быть как -

enter image description here

Я не могу придумать элегантное решение для этого.

возможно ли использовать только LINQ? Если нет, как мы можем достичь этого, используя LINQ с некоторым кодом C#.

4 ответов


вы можете сделать это с помощью комбинации loop и Enumerable.Take, например:

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;
}

где ваш list определено как:

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"},

    };

и User класс as:

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

позже вы можете распечатать вывод, как:

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

вы получаете:

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

можно ли это сделать с помощью LINQ? Наверное, не так просто. Можно ли это сделать с помощью собственного метода расширения довольно легко? Конечно (я на самом деле не пытался скомпилировать и запустить код, поэтому я не могу гарантировать, что это сработает, но это определенно хорошая отправная точка):

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]);
    }
}

вы можете сделать это без двойной итерацией набор такой:-

        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);

Я сделал это с помощью Select. Я писал функцию синхронизации, которая получала элементы из World of Warcraft API, и некоторые элементы больше не были в API, хотя технически они все еще были в игре. Поскольку я синхронизировал предметы в порядке количества раз, когда они были перечислены на аукционе, он застрял бы на этих определенных предметах.

Я создал список плохих идентификаторов элементов. Если элемент не синхронизировался, я добавлял его в список. Я хотел пропустить элемент, если он не может синхронизировать 3 раз. Я выполнил это со следующим кодом:

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();
}