HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Grouping reviews by UserId

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
reviewsuseridgrouping

Problem

The class I'm using is called Review and looks like this:

public class Review
{
    public virtual Guid Id { get; set; }

    public virtual int Rating { get; set; }

    public virtual Guid RestaurantId { get; set; }

    public virtual Guid UserId { get; set; }
}


I'm using a LINQ statement to first check a few things and afterwards group by a user's reviews. I'll explain what I mean with this:

var reviewsByUsers = 
    reviews
    .GroupBy(x => x.RestaurantId)
    .Where(x => x.Count() >= 3)
    .SelectMany(x => x.ToList())
    .Distinct()
    .GroupBy(x => x.UserId)
    .Where(x => x.Count() >= 2)
    .Select(x => x.ToList())
    .ToList();


And now step by step:

  1. reviews is a list of all reviews given on a bunch of restaurants. This is not the database, it's a list I receive as a parameter of the method.



  1. .GroupBy(x => x.RestaurantId) Group these reviews per each restaurant. This is for counting them later.



  1. .Where(x => x.Count() >= 3) Exclude any restaurants which don't have 3 or more reviews on them.



  1. .SelectMany(x => x.ToList()) Flatten the list so we can group again.



  1. .Distinct() To be sure that I'm not getting doubles.



  1. .GroupBy(x => x.UserId) Group the reviews per user. This is for counting them later.



  1. .Where(x => x.Count() >= 2) Exclude any user which has less than 2 reviews.



  1. .Select(x => x.ToList()) I don't need anything else per user, a list of reviews per user is enough.



All of this will result in a List> which is a list that contains a list of all reviews grouped by the UserId.

I'm certain there has to be a better way to perform these checks and create a list of lists grouped by user id. I'm just very new to C#, so I don't know how to increase this LINQ statement.

Solution

There is actaully another solution that utilizes the Except extension. This means you first select all reviews with at least two users but remove those where the restaurant does not have at least three reviews.

This requires to write a new IEqualityComparer that I find is an overkill for this but on the other hand the Except clearly tells what kind of results we don't want to have. Anyway, the alternative solution:

reviews
    .GroupBy(r => r.UserId)
    .Where(g => g.Count() > 1)
    .Except(
        reviews
            .GroupBy(r => r.RestaurantId)
            .Where(g => g.Count() < 3),
        new ReviewComparer()
    );


where

class ReviewComparer : IEqualityComparer>
{
    public bool Equals(IGrouping left, IGrouping right) 
    {
        // left - user-group
        // right - restaurant-group
        // exclude the user group 
        // if it contains any restaurant-id of the excluded restaurant-ids
        return left.Any(x => right.Any(y => y.Id == x.Id));
    }

    public int GetHashCode(IGrouping reviews)
    {
        return 0; // treat all groups equally
    }
}

Code Snippets

reviews
    .GroupBy(r => r.UserId)
    .Where(g => g.Count() > 1)
    .Except(
        reviews
            .GroupBy(r => r.RestaurantId)
            .Where(g => g.Count() < 3),
        new ReviewComparer()
    );
class ReviewComparer : IEqualityComparer<IGrouping<Guid, Review>>
{
    public bool Equals(IGrouping<Guid, Review> left, IGrouping<Guid, Review> right) 
    {
        // left - user-group
        // right - restaurant-group
        // exclude the user group 
        // if it contains any restaurant-id of the excluded restaurant-ids
        return left.Any(x => right.Any(y => y.Id == x.Id));
    }

    public int GetHashCode(IGrouping<Guid, Review> reviews)
    {
        return 0; // treat all groups equally
    }
}

Context

StackExchange Code Review Q#151750, answer score: 6

Revisions (0)

No revisions yet.