1

There is a similar question that doesn't answer my question. --> Count number of element in List>

I have a list which contains sublists:

List<string> sublist1 = new List<string>() { "a", "b" };
List<string> sublist2 = new List<string>() { "a", "b" };
List<string> sublist3 = new List<string>() { "a", "c" };

Now I want to count the occurrences of each list.

a, b --> 2
a, c --> 1

I used distinct() from LINQ, but I got the output:

a, b --> 1
a, b --> 1
a, c --> 1

I assume that the hashcode is different. Is there an alternative to distinct() which is looking at the list values instead? I want to solve this in LINQ if possible.

Edit: The order of list items has to be the same!

kame
  • 20,848
  • 33
  • 104
  • 159
  • Looks like a job for `GroupBy()` – Matthew Watson Nov 12 '19 at 09:20
  • @MatthewWatson I tried this, but `GroupBy()` also return three unique elements. :/ – kame Nov 12 '19 at 09:21
  • distinct will remove the dups, so for count you need to use groupby+count – Siavash Rostami Nov 12 '19 at 09:23
  • Maybe I delete the question here and ask how to GroupBy() a list of lists!?!?!? – kame Nov 12 '19 at 09:33
  • 3
    You might need to write your own Comparer. Somewhere in the back of my mind is the idea that LINQ compares objects like `List` based on their reference. That's why `distinct()` still gives you three items. – Markus Deibel Nov 12 '19 at 09:36
  • Are these two lists equal? `new List() { "a", "b" }`, `new List() { "b", "a" }` – Theodor Zoulias Nov 12 '19 at 09:40
  • @TheodorZoulias The order is important!!! Please remove the downvote. :) – kame Nov 12 '19 at 09:45
  • 1
    I can't remove other people's downvotes. – Theodor Zoulias Nov 12 '19 at 09:45
  • @TheodorZoulias So this is no duplicate! – kame Nov 12 '19 at 09:46
  • Possible duplicate: [Remove duplicate lists inside a list of lists in C#](https://stackoverflow.com/questions/54551181/remove-duplicate-lists-inside-a-list-of-lists-in-c-sharp) – Theodor Zoulias Nov 12 '19 at 09:50
  • Possible duplicate: https://stackoverflow.com/questions/44737685/linq-to-get-distinct-count-sort-in-listliststring – Matthew Watson Nov 12 '19 at 10:15
  • @MatthewWatson Yes this is finally what I was looking for. My own solution also works. Should I delete this post now? – kame Nov 12 '19 at 10:39
  • @kame I discovered that dupe after I posted my answer (which also implements `IEqualityComparer>`) - however, I think you should use my version simply because I added null checking to it - the other answer will fail if there are any null strings in the lists. (Technically the other implementation could also fail if checking for arithmetic overflow is enabled, whereas my implementation explicity uses `unchecked` to avoid that issue.) – Matthew Watson Nov 12 '19 at 11:01

5 Answers5

2

To use GroupBy() to do this, you will need a suitable IEqualityComparer<List<string>> that compares lists of strings. There is no built-in implementation, so you have to roll your own:

public sealed class StringListEqualityComparer : IEqualityComparer<List<string>>
{
    public bool Equals(List<string> x, List<string> y)
    {
        if (ReferenceEquals(x, y))
            return true;

        if (x == null || y == null)
            return false;

        return x.SequenceEqual(y);
    }

    public int GetHashCode(List<string> strings)
    {
        int hash = 17;

        foreach (var s in strings)
        {
            unchecked
            {
                hash = hash * 23 + s?.GetHashCode() ?? 0;
            }
        }

        return hash;
    }
}

Once you've got that, you can use it with GroupBy() as follows:

public static void Main()
{
    var sublist1 = new List<string>{ "a", "b" };
    var sublist2 = new List<string>{ "a", "b" };
    var sublist3 = new List<string>{ "a", "c" };

    var listOfLists = new List<List<string>> {sublist1, sublist2, sublist3};

    var groups = listOfLists.GroupBy(item => item, new StringListEqualityComparer());

    foreach (var group in groups)
    {
        Console.WriteLine($"Group: {string.Join(", ", group.Key)}, Count: {group.Count()}");
    }
}
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
1
     public JsonResult CountList(){
        List<List<string>> d = new List<List<string>>(); //SuperList
            d.Add(new List<string> { "a", "b" }); //List 1
            d.Add(new List<string> { "a", "b" }); // List 2
            d.Add(new List<string> { "a", "c" }); // List 3
            d.Add(new List<string> { "a", "c", "z" }); //List 4
            var listCount = from items in d
                            group items by items.Aggregate((a,b)=>a+""+b) into groups
                            select new { groups.Key, Count = groups.Count() };
        return new JsonResult(listCount);
     }

This will give the following Result as output in Post Man or Advanced REST Client

[{
"key": "ab",
"count": 2
},
  {
"key": "ac",
"count": 1
},
  {
"key": "acz",
"count": 1
}],
leapdev
  • 34
  • 5
1

I think this will be helpful

 var list = new List<List<string>>() { sublist1, sublist2, sublist3};
 var result = list.GroupBy(x => string.Join(",",x)).ToDictionary(x => x.Key.Split(',').ToList(), x => x.Count());
Furkan Öztürk
  • 1,178
  • 11
  • 24
1

You can try the below code:-

            List<string> sublist1 = new List<string>() { "a", "b" };   
            List<string> sublist2 = new List<string>() { "a", "b" };   
            List<string> sublist3 = new List<string>() { "a", "c" };   
            List<List<string>> listOfLists = new List<List<string>> { sublist1, sublist2, sublist3 };    
            Dictionary<string, int> counterDictionary = new Dictionary<string, int>();                  
                foreach (List<string> strList in listOfLists)       
                {   
                    string concat = strList.Aggregate((s1, s2) => s1 + ", " + s2);   
                    if (!counterDictionary.ContainsKey(concat))   
                        counterDictionary.Add(concat, 1);   
                    else   
                        counterDictionary[concat] = counterDictionary[concat] + 1;   
                }   
                foreach (KeyValuePair<string, int> keyValue in counterDictionary)   
                {   
                       Console.WriteLine(keyValue.Key + "=>" + keyValue.Value);   
                }   
Darpan Gupta
  • 181
  • 2
0

I think I will solve this with:

var equallists = list1.SequenceEqual(list2);

Therefore I compare distinct lists and lists with SequenceEquals() and counting them.

Better solutions welcome. :)

kame
  • 20,848
  • 33
  • 104
  • 159