Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array distinct lambda comparisons with preference

I have data with the following shape

someArray = [{ Name: "Some Class", TypeEnum: "Default" },
 { Name: "Some Class", TypeEnum: "Other" },
 { Name: "Some Class 2", TypeEnum: "Default" },
 { Name: "Some Class 2", TypeEnum: "Other" },
 { Name: "Some Class 3", TypeEnum: "Default" },
 { Name: "Some Class 4", TypeEnum: "Not Other" }]

Imagine each of those as objects in C#

What I need is an array of distinct versions of that array, with preference given to a selected TypeEnum. For example if I have selected the TypeEnum of other, I still want it to default to Default if it can't find a version of that class with the "Other" TypeEnum

e.g. With "Other" selected as the Type enum, the above data would look like

 [{ Name: "Some Class", TypeEnum: "Other" },
 { Name: "Some Class 2", TypeEnum: "Other" },
 { Name: "Some Class 3", TypeEnum: "Default" }]

What I'm doing now is a lambda comparison from here

TypeEnum myEnum = "Other"
someArray.Distinct((x,y) => x.Name == y.Name && 
                   x.TypeEnum != myEnum && 
                   (y.TypeEnum == myEnum || y.TypeEnum == "Default"));

I'm hoping that Distinct pops out any x from the array that get's a true from that expression.

Am I wrong in how I think Distinct works. If I am, what should I use instead?

like image 954
DrSammyD Avatar asked Jul 17 '26 22:07

DrSammyD


1 Answers

You can define a Comparer<T> class to handle your preference for comparison, like this:

public class SomeClassComparer : Comparer<SomeClass>
{
    private TypeEnum _preference;

    public SomeClassComparer(TypeEnum preference)
        : base()
    {
        _preference = preference;
    }

    public override int Compare(SomeClass x, SomeClass y)
    {
        if (x.Name.Equals(y.Name))
        {
            return x.TypeEnum == y.TypeEnum ? 0
                : x.TypeEnum == _preference ? -1
                : y.TypeEnum == _preference ? 1
                : x.TypeEnum == TypeEnum.Default ? -1
                : y.TypeEnum == TypeEnum.Default ? 1
                : x.TypeEnum.CompareTo(y.TypeEnum);
        }
        else
            return x.Name.CompareTo(y.Name);
    }
}

UPDATE: If you're only interested in the elements with your preferred or Default TypeEnum, you could filter out the rest first. Then sort the array according to the Comparer, i.e. giving preferred TypeEnum higher precedence than Default. Finally group the objects by their Name, and take the first one from each group:

var result = someArray.Where(x => x.TypeEnum == TypeEnum.Default || x.TypeEnum == myEnum)
                      .OrderBy(x => x, new SomeClassComparer(myEnum))
                      .GroupBy(x => x.Name)
                      .Select(x => x.First());

Or you can use the following version if you don't want to define a Comparer class:

Comparison<SomeClass> compareByTypeEnum = (x, y) =>
{
    if (x.Name.Equals(y.Name))
    {
        return x.TypeEnum == y.TypeEnum ? 0
            : x.TypeEnum == myEnum ? -1
            : y.TypeEnum == myEnum ? 1
            : x.TypeEnum == TypeEnum.Default ? -1
            : y.TypeEnum == TypeEnum.Default ? 1
            : x.TypeEnum.CompareTo(y.TypeEnum);
    }
    else
        return x.Name.CompareTo(y.Name);
};
Array.Sort(someArray, compareByTypeEnum);
var result = someArray.Where(x => x.TypeEnum == TypeEnum.Default || x.TypeEnum == TypeEnum.Other)
                      .GroupBy(x => x.Name)
                      .Select(x => x.First());
like image 125
Fung Avatar answered Jul 20 '26 12:07

Fung