Linq группа по нескольким, а также пустым значениям - PullRequest
0 голосов
/ 13 марта 2020

Мне нужно иметь возможность группировать по нескольким значениям, но также совпадать, когда значение пустое.

    public class ExampleClass
    {
        public string FieldX { set; get; }
        public string FieldY { set; get; }
        public string FieldZ { set; get; }
        public string FieldA { set; get; }
        public string FieldB { set; get; }
        public string FieldC { set; get; }
    }

    var obj1 = new ExampleClass{
            FieldA = "AAA",
            FieldB = "BBB",
            FieldC = "CCC",
            FieldX = "Matched",
            FieldY = "Matched",
            FieldZ = "Matched"
        };

    var obj2 = new ExampleClass{
        FieldA = "ada a",
        FieldB = "BBBada ",
        FieldC = "CCadasd aC",
        FieldX = "Matched",
        FieldY = "Matched",
        FieldZ = "Matched"
    };
    var obj3 = new ExampleClass{
        FieldA = "AfsfAA",
        FieldB = "BBsfsfB",
        FieldC = "CsfsghsCC",
        FieldX = "",
        FieldY = "Matched",
        FieldZ = "Matched"
    };

    var obj4 = new ExampleClass{
        FieldA = "AAA",
        FieldB = "BBB",
        FieldC = "CCC",
        FieldX = "Not Matched",
        FieldY = "Not Matched",
        FieldZ = "Matched"
    };
    var list = new List<ExampleClass>(new ExampleClass[] { obj1, obj2, obj3, obj4 } );
    var grp = list.GroupBy(x => new { x.FieldX, x.FieldY, x.FieldZ });

Так что теперь grp равен

//grp = [{[obj1, obj2]}, {[obj3]}, {[obj4]}]

Но мне нужно, чтобы grp равнялся

//grp = [{[obj1, obj2, obj3]}, {[obj4]}]

Надеюсь, есть эффективный способ добиться этого без многократных вложенных циклов.

Большое спасибо.

1 Ответ

2 голосов
/ 13 марта 2020

Я думаю, что это может помочь вам (я добавляю do tnet URL скрипты , чтобы вы могли поиграть с ним)

using System;
using System.Linq;
using System.Collections.Generic;

public class Program
{
    public class ExampleClass
    {
        public string FieldX { set; get; }
        public string FieldY { set; get; }
        public string FieldZ { set; get; }
        public string FieldA { set; get; }
        public string FieldB { set; get; }
        public string FieldC { set; get; }
    }

    public class IncludeBlankComparer : IEqualityComparer<(string a, string b, string c)>
    {
        public static bool IsBlank(string s) => string.IsNullOrEmpty(s);

        private static bool IsMatchOrBlankMatch(string left, string right) => left == right || IsBlank(left) || IsBlank(right);

        public bool Equals((string a, string b, string c) first, (string a, string b, string c) second)
        { 
            if (first == second) return true;
            return IsMatchOrBlankMatch(first.a, second.a)
                && IsMatchOrBlankMatch(first.b, second.b)
                && IsMatchOrBlankMatch(first.c, second.c);
        }

        public int GetHashCode((string a, string b, string c) s) => 0;
    }

    public static void Main()
    {
        var obj1 = new ExampleClass{
                FieldA = "AAA",
                FieldB = "BBB",
                FieldC = "CCC",
                FieldX = "Matched",
                FieldY = "Matched",
                FieldZ = "Matched"
            };

        var obj2 = new ExampleClass{
            FieldA = "ada a",
            FieldB = "BBBada ",
            FieldC = "CCadasd aC",
            FieldX = "Matched",
            FieldY = "Matched",
            FieldZ = "Matched"
        };
        var obj3 = new ExampleClass{
            FieldA = "AfsfAA",
            FieldB = "BBsfsfB",
            FieldC = "CsfsghsCC",
            FieldX = "",
            FieldY = "Matched",
            FieldZ = "Matched"
        };

        var obj4 = new ExampleClass{
            FieldA = "AAA",
            FieldB = "BBB",
            FieldC = "CCC",
            FieldX = "Not Matched",
            FieldY = "Not Matched",
            FieldZ = "Matched"
        };

        var list = new List<ExampleClass>(new ExampleClass[] { obj1, obj2, obj3, obj4 } );
        var grp = list.GroupBy(x => ( x.FieldX, x.FieldY, x.FieldZ ), new IncludeBlankComparer());

        grp.Dump();
    }
}

Объяснение : Вы можете передать пользовательский компаратор в метод GroupBy. Конечно, компаратор должен обрабатывать сравнение тех же типов, что и ключ. Обратите внимание, что я изменил ключевой селектор с анонимного класса (в котором я не могу ссылаться на внутренних членов) на кортеж (в котором я могу).

Примечание : это решение будет работать только с ключом кортежа, который имеет 3 строковых части.

Примечание 2 : из-за хакерской природы этого решения мне пришлось использовать GetHashCode() => 0. Это будет работать, поскольку реализация поиска сравнивает как на равенство, так и на равенство хеш-кодов, и не нарушит реализацию GroupBy, однако само по себе это не "чистое" решение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...