Group objects in List<T> by id and order the List by duplicateCount for each object

13,107

Solution 1

This will work like you requested:

var list1 = new[] { "u1", "u2", "u3", "u4" };
var list2 = new[] { "u2", "u4", "u5" };
var list3 = new[] { "u4", "u6" };

var allLists = list1.Concat(list2).Concat(list3);

var result = from u in allLists
             group u by u into g
             orderby g.Count() descending 
             select g.Key;

And a version with an object instead of string

var list1 = new[] { new User("u1"), new User("u2"), new User("u3"), new User("u4") };
var list2 = new[] { new User("u2"), new User("u4"), new User("u5") };
var list3 = new[] { new User("u4"), new User("u6") };

var allLists = list1.Concat(list2).Concat(list3);

var result = from u in allLists
                group u by u.Name into g
                orderby g.Count() descending 
                select g.Key;

Edit: Updated code samples projecting g.Key instead of only g.

And here is the Method Chain equivalent of the query above:

var result = allLists
    .GroupBy(u => u.Name)
    .OrderByDescending(g => g.Count())
    .Select(g => g.Key);

Solution 2

Done with strings, but should also work with objects:

var l = new string[] { "user1", "user2", "user3", "user4",
    "user2", "user4", "user5" ,
    "user4", "user6" };

var result = l.Aggregate(new Dictionary<string, int>(),
    (res, user) =>
    { // create a dictionary of all users and their occurence
        if (!res.ContainsKey(user)) res[user] = 0;
        res[user]++;
        return res;
    }).OrderByDescending(kvp => kvp.Value) // order by incidence
    .Select(kvp => kvp.Key); // select user names only

foreach (var user in result) Console.WriteLine(user);
Share:
13,107
ThdK
Author by

ThdK

Web developer and photographer

Updated on June 04, 2022

Comments

  • ThdK
    ThdK almost 2 years

    I have multiple List with objects. But some of the items in one list, also exists in other lists. My question is how can I merge all the items of all the lists into one final list. So there are no duplicates in that list and the items are sorted on the number of duplicates each item had in the different lists.

    Example

    List1: [users1, user2, user3, user4]
    List2: [user2, user4, user5]
    List3: [user4,user6]

    result: [user4, user2, user1, user3, user5, user6]

    (the order of users with the same count doesn't matter)

    I tried something like this:

    List<User> finalResults = list1.Concat(list2).ToList<User>();
    
            var q = finalResults.GroupBy(x => x.id)
                        .Select(g => new User { name = g.First().name, count = g.Count() })
                        .OrderByDescending(usr => usr.count);
    
            finalResults = q.ToList<User>();
    

    but the result is an empty list.

    Thanks in advance!

  • Nasmi Sabeer
    Nasmi Sabeer about 13 years
    @MikeEast projection should be g.Key
  • Steven Jeuris
    Steven Jeuris about 13 years
    Strange, I would expect this solution to be less performant than paulmey's solution, however, it's the other way around. (Not that clarity doesn't win as long as performance isn't an issue.) I was just wondering what this would compile to which is more performant than the other solution.
  • Mikael Östberg
    Mikael Östberg about 13 years
    @Steven, I don't think there is such a big performance difference. This might even be performing better.
  • Steven Jeuris
    Steven Jeuris about 13 years
    @MikeEast: It is performing better. ;p (at least for big collections on my pc)
  • Mikael Östberg
    Mikael Östberg about 13 years
    @Steven, Mine too: 4741ms vs 3467ms over 1M iterations on the collection in the example.
  • Mikael Östberg
    Mikael Östberg about 13 years
    @Steven, It's probably the grouping that requires the extra time. Anyway - for any normal usage this is not a big difference. And for the cases you depend on that difference, I think you have chosen the wrong language to begin with. :)