Linq GroupBy с каждым нулевым значением в виде группы

У меня есть объект с нулевым свойством int "GroupId".

Со списком этого объекта я хотел бы сделать GroupBy на этом "GroupId". Но если я это сделаю, все нулевые значения образуют группу.

Пример:

Объект 1: GroupId: NULL

Объект 2: GroupId: NULL

Объект 3: GroupId: 1

Объект 4: GroupId: 1

Объект 5: GroupId: 2

Объект 6: GroupId: 2

MyList.GroupBy(f => f.GroupId, key => new {Object = key});

Я получу 3 группы.

Как я могу получить 4 группы? Группа для каждого значения NULL...

Ответ 1

Это, вероятно, самое короткое решение:

var grouped = MyList.GroupBy(f => f.GroupId != null ? (object)f.GroupId : new object(), key => new { Object = key });

Обратите внимание, что "ключ" групп будет иметь тип object. Для элементов null я создаю новый "пустой" object. Сопоставитель равенства будет делать так, чтобы они были разными. Для непустых чисел я просто помещаю их в объект. Целые числа в штучной упаковке сохраняют оператор равенства. Итак:

new object().Equals(new object()) == false // always

и

((object)1).Equals((object)1) == true // always

и

((object)1).Equals((object)2) == false // always

более правильным решением будет реализация IEqualityComparer<int?>

public class MyComparer : IEqualityComparer<int?> {
    public bool Equals(int? x, int? y) {
        if (x == null || y == null) {
            return false;
        }

        return x.Value == y.Value;
    }

    public int GetHashCode(int? obj) {
        return obj.GetHashCode(); // Works even if obj is null :-)
    }
}

и используя его:

var grouped2 = MyList.GroupBy(f => f.GroupId, key => new { Object = key }, new MyComparer());

Ответ 2

Общий Comparer, который можно использовать без бокса.

public class NullableComparer<T> : IEqualityComparer<T?>
        where T : struct
{
    public bool Equals(T? x, T? y)
    {
        if (x == null || y == null)
        {
            return false;
        }

        return x.Equals(y);
    }

    public int GetHashCode(T? obj)
    {
        return obj.GetHashCode();
    }
}

Затем вы будете использовать его, как:

// where GroupId as a nullable Guid 
var grouped = MyList.GroupBy(f => f.GroupId, new NullableComparer<Guid>());