17

I'm looking for the LINQ equivalent to the Sybase's LIST() or MySQL's group_concat()

It'll convert:

User  Hobby
--------------
Bob   Football 
Bob   Golf 
Bob   Tennis 
Sue   Sleeping 
Sue   Drinking

To:

User  Hobby
--------------
Bob   Football, Golf, Tennis 
Sue   Sleeping, Drinking

6 Answers 6

16

That's the GroupBy operator. Are you using LINQ to Objects?

Here's an example:

using System;
using System.Collections.Generic;
using System.Linq;

public class Test
{
    static void Main()
    {
        var users = new[]
        {
            new { User="Bob", Hobby="Football" },
            new { User="Bob", Hobby="Golf" },
            new { User="Bob", Hobby="Tennis" },
            new { User="Sue", Hobby="Sleeping" },
            new { User="Sue", Hobby="Drinking" },
        };

        var groupedUsers = users.GroupBy(user => user.User);

        foreach (var group in groupedUsers)
        {
            Console.WriteLine("{0}: ", group.Key);
            foreach (var entry in group)
            {
                Console.WriteLine("  {0}", entry.Hobby);
            }
        }
    }
}

That does the grouping - can you manage the rest yourself?

Sign up to request clarification or add additional context in comments.

3 Comments

It's also possible to replace the inner loop with String.Join(", ", group.ToArray()), as long as the number of elements is not very large.
Thanks! I'm going to learn LINQ myself instead of constantly bugging you ;-)
Am I the only one that noticed the man is into sports while the woman's hobbies are sleeping and drinking? You're terrible Jon Skeet!
5

See if this solution helps you:

List<User> users = new List<User>() 
{ 
    new User {Name = "Bob", Hobby = "Football" },
    new User {Name = "Bob", Hobby = "Golf"},
    new User {Name = "Bob", Hobby = "Tennis"},
    new User {Name = "Sue", Hobby = "Sleeping"},
    new User {Name = "Sue", Hobby = "Drinking"}
};

var groupedUsers = from u in users
         group u by u.Name into g
         select new
         {
             Name = g.First<User>().Name,
             Hobby = g.Select(u => u.Hobby)
         };


foreach (var user in groupedUsers)
{
    Console.WriteLine("Name: {0}", user.Name);
    foreach (var hobby in user.Hobby)
    {
        Console.WriteLine("Hobby: {0}", hobby);
    }
}

Comments

5

re the _concat aspect of your question, using:

static class EnumerableExtensions 
{  
    public static String AsJoined( this IEnumerable<String> enumerable )
    {
        return AsJoined( enumerable, "," );
    }

    public static String AsJoined( this IEnumerable<String> enumerable, String separator )
    {
        return String.Join( separator, enumerable.ToArray() );
    }
}

The outputting foreach in bruno conde and Jon Skeet's answers can become:

Console.WriteLine( "User:\tHobbies");
foreach ( var group in groupedUsers )
    Console.WriteLine( "{0}:\t{1}", group.Key, group.Select( g => g.Hobby ).AsJoined( ", " ) );

... and you'll get the precise result output format you asked for (yes, I know the others have already solved your problem, but its hard to resist!)

3 Comments

this is like MySQL's GROUP_CONCAT function. useful.
@Moshe L I guess these days I might replace group.Select( g => g.Hobby ).AsJoined( ", " ) with String.Join(",", from g in group select g.Hobby) (This requires the overload of String.Join which takes IEnumerable<T>, which was introduced in .NET 4.0)
or just use on 3.5 group.Select( g => g.Hobby ).ToArray().
3

Or else we can do the following-

var users = new[]
                {
                new { User="Bob", Hobby="Football" },
                new { User="Bob", Hobby="Golf" },
                new { User="Bob", Hobby="Tennis" },
                new { User="Sue", Hobby="Sleeping" },
                new { User="Sue", Hobby="Drinking" },
                };

                var userList = users.ToList();
                var ug = (from user in users
                          group user by user.User into groupedUserList
                          select new { user = groupedUserList.Key, hobby = groupedUserList.Select(g =>g.Hobby)});

                var ug2 = (from groupeduser in ug
                          select new{ groupeduser.user, hobby =string.Join(",", groupeduser.hobby)});

Comments

2

To do it in one Linq Statement. There is no way I'd recommend the code, but it shows that it could be done.

            var groupedUsers = from user in users
                           group user by user.User into userGroup
                           select new
                           {
                               User = userGroup.Key,
                               userHobies =
                                   userGroup.Aggregate((a, b) => 
                                       new { User = a.User, Hobby = (a.Hobby + ", " + b.Hobby) }).Hobby
                           }
                            ;
        foreach (var x in groupedUsers)
        {
            Debug.WriteLine(String.Format("{0} {1}", x.User, x.userHobies));
        }

Comments

1

all answers is not good enough;

because this is a db query,but all of us do that just in memory;

diff is that some operation in memory will occuce a error can't trans to store expression;

var list = db.Users.GroupBy(s=>s.User).
             select(g=>new{user=g.Key,hobbys=g.select(s=>s.Hobby)}); // you can just do that from db

var result=list.ToList(); // this is important,to query data to memory;

var result2 = result.select(g=>new{user=g.Key,hobbyes=string.join(",",g.hobbyes)}; //then,do what you love in memory

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.