Linq distinct based on two columns

2019-07-15 02:59发布

问题:

I've a requirement where I need to fetch the unique records with same combination in 2 columns. My data would be Like CA(Column A) and CB(Column B) with some data

CA CB
1 2
1 2
3 4
5 6
2 1
1 6
1 6
5 1

Let's say, I need to fetch records with value 1 from both the columns which should be unique.

So, My End result should be like :

1 2
1 6
5 1

Here I should not get the record 2,1 because the combination already exists as 1,2 in the first record.

Here's the query I've tried :

var recentchats = (from s in MessagesCollection.AsQueryable()
                    where (s.@from == mytopic || s.to == mytopic) 
                    orderby s._id descending
                    select s).DistinctBy(x => x.from).Take(10).ToList();

I've used moreLinq extension for DistinctBy, because I need the whole record.(sorry for bad formatting and english!!!)

Here, My actual requirement is getting the recent chats of a user

回答1:

Since the where has already made sure one of the two values is always the same, you can use the sum in distinctBy. (e.g. 1 + 2 is equal to 2 + 1)

DistinctBy(x => x.from + x.to)

Without the where, you can use Min and Max to still get unique pairs.

DistinctBy(x => new { Min=Math.Min(x.from, x.to), Max=Math.Max(x.from, x.to) })


回答2:

So you need a way to detect duplicates according to multiple columns and the order doesn't matter? You could use this class:

public class MultiFieldIgnoreOrderComparer : IEquatable<IEnumerable<object>>, IEqualityComparer<IEnumerable<object>>
{
    private IEnumerable<object> objects;

    public MultiFieldIgnoreOrderComparer(IEnumerable<object> objects)
    {
        this.objects = objects;
    }

    public bool Equals(IEnumerable<object> x, IEnumerable<object> y)
    {
        return x.All(y.Contains);
    }

    public int GetHashCode(IEnumerable<object> objects)
    {
        unchecked
        {
            int detailHash = 0;
            unchecked
            {
                // order doesn't matter, so we need to order:
                foreach (object obj in objects.OrderBy(x => x))
                    detailHash = 17 * detailHash + (obj == null ? 0 : obj.GetHashCode());
            }
            return detailHash;
        }
    }

    public override int GetHashCode()
    {
        return GetHashCode(this.objects);
    }

    public override bool Equals(object obj)
    {
        MultiFieldIgnoreOrderComparer other = obj as MultiFieldIgnoreOrderComparer;
        if (other == null) return false;
        return this.Equals(this.objects, other.objects);
    }

    public bool Equals(IEnumerable<object> other)
    {
        return this.Equals(this.objects, other);
    }
}

You can use it in this way:

var recentchats = MessagesCollection.AsQueryable()
    .Where(x => x.CA == 1 || x.CB == 1)
    .GroupBy(x => new MultiFieldIgnoreOrderComparer(new[] { x.CA, x.CB }))
    .Select(g => g.First())
    .ToList();


标签: c# linq morelinq