testpattern
testpattern

Reputation: 2498

C# linq group by on different keys in the same entity

I've been toying with this for a while and just can't get it. I'm new to Linq, C# and these Lambda things.

What I want to do is group entities according to two properties on each entity. It's a Message entity:

Message
{
int UserId; //The user generating the message
int UserIdTo; //The receiver of the message
|...| // Other stuff 
}

So, I want it so that these UserId=5, UserIdTo=6 and UserId=6, UserIdTo=5 would be in the same group.

Here's my start:

var groupList = (from m in db.Messages
                             where m.UserId == userId || m.UserIdTo == userId
                             join u in db.Users on m.UserId equals u.UserId
                             join w in db.Users on m.UserIdTo equals w.UserId
                             orderby m.MessageTimestamp descending
                             select new DomMessage
                             {
                                 MessageId = m.MessageId,
                                 MessageContent = m.MessageContent,
                                 MessageTimestamp = m.MessageTimestamp,
                                 UserId = m.UserId,
                                 UserIdTo = m.UserIdTo,
                                 ScreenName = u.ScreenName,
                                 ScreenName2 = w.ScreenName
                             }).GroupBy(m=>m.UserId == userId)
                             .ToList();

This does the first bit of grouping by UserId, but I'm stuck on trying to extend this so that where any UserId value in the resulting group equals the UserIdTo somewhere else add that to this group?

EDIT: I need the result to go to a List because there is other stuff I need to do with it...

Thanks!

Upvotes: 4

Views: 1828

Answers (3)

VenerableAgents
VenerableAgents

Reputation: 656

I think this is the easiest way:

var groupList = from a in (
from m in db.Messages
where m.UserId == userId || m.UserIdTo == userId
join u in db.Users on m.UserId equals u.UserId
join w in db.Users on m.UserIdTo equals w.UserId
select new 
{
 MessageId = m.MessageId,
 MessageContent = m.MessageContent,
 MessageTimestamp = m.MessageTimestamp,
 UserId = m.UserId,
 UserIdTo = m.UserIdTo,
 ScreenName = u.ScreenName,
 ScreenName2 = w.ScreenName
})
group a by new { 
    UserId = a.UserId,
     UserIdTo = a.UserIdTo
    } into grp
orderby grp.Max(a => a.MessageTimestamp) descending
select new
{
 UserId = grp.Key.UserId
 UserIdTo = grp.Key.UserIdTo,
 MessageId = grp.Max(a => a.MessageId),
 MessageContent = grp.Max(a => a.MessageContent),
 MessageTimestamp = grp.Max(a => a.MessageTimestamp),
 ScreenName = grp.Max(a => a.ScreenName),
 ScreenName2 = grp.Max(a => a.ScreenName2)
}

You have to tell it what to do with the fields you are not grouping by. In this case I got the MAX value for each.

Upvotes: 0

Adrian Salazar
Adrian Salazar

Reputation: 5319

Try this:

var payload = new[] 
        {
            new{ To = 1, From = 2, Message = "msj1" },
            new{ To = 1, From = 2, Message = "msj2" },
            new{ To = 2, From = 1, Message = "msj3" },
            new{ To = 4, From = 1, Message = "msj4" },
            new{ To = 1, From = 3, Message = "msj5" }
        };


        var groupped = payload.Select(x => new { Key = Math.Min(x.To, x.From) + "_" + Math.Max(x.To, x.From), Envelope = x }).GroupBy(y => y.Key).ToList();

        foreach (var item in groupped)
        {
            Console.WriteLine(String.Format(@"Group: {0}, messages:", item.Key));

            foreach (var element in item)
            {
                Console.WriteLine(String.Format(@"From: {0} To: {1} Message: {2}", element.Envelope.From, element.Envelope.To, element.Envelope.Message));
            }
        }

Upvotes: 2

Rich O'Kelly
Rich O'Kelly

Reputation: 41757

Try the following GroupBy expression:

.GroupBy(m => Math.Min(m.UserId, m.UserIdTo) + ',' + Math.Max(m.UserId, m.UserIdTo))

Upvotes: 1

Related Questions