C # GroupBy динамические значения - неизвестные параметры - C # lambda GroupBy EqualityComparer - PullRequest
0 голосов
/ 13 июня 2019

Я бы хотел, чтобы GroupBy собирал по определенным столбцам, но они могут различаться, и я не хотел бы жестко задавать поля, для которых будет иметь место группировка.

Я пытался:

users.GroupBy(groupingColumnIndexes.Select(a => x.ColumnValues[a])) 

...

List<Row> users = new List<Row>()
        {
            new Row("John", "Doe", "10"),
            new Row("John", "Doe", "45"),
            new Row("Will","Smith", "26"),
            new Row("Will", "Smith", "52"),
            new Row("Julius", "Cesar", "23")
        };

        List<int> groupingColumnIndexes = new List<int>() { 0, 1 };

        List<User> output = users
            .GroupBy(x => {
                    // INFO: if I'd return groupingColumns code would not group users correctly.
                    var groupingColumns = groupingColumnIndexes.Select(a => x.ColumnValues[a]);

                    string col1Value = x.ColumnValues[0];
                    string col2Value = x.ColumnValues[1];

                    // Result below is working, but I would rather build more dynamic version.
                    var result =  new { col1Value, col2Value };
                    return result;
                })
            .Select(x => new User
            {
                Name = string.Join(",", x.Key),
                Age = (int)x.Sum(a => int.Parse(a.ColumnValues[2])),
                LastName = string.Empty
            }).ToList();

.NET Fiddle: https://dotnetfiddle.net/cPuafD

Ожидается: Джон Доу 55 Уилл Смит 78 Джулиус Сезар 23

Актуально при использовании GroupBy (список): Джон, Доу 10 Джон, Доу 45 Уилл, Смит 26 Уилл, Смит 52 Юлий, Сезар 23

Ответы [ 2 ]

1 голос
/ 13 июня 2019

I улучшено немного @ Ответ Xiaoy312, чтобы его было легче понять.

public class StringColumnEqualityComparer : IEqualityComparer<List<string>>
{
    public StringColumnEqualityComparer()
    {

    }

    public bool Equals(List<string> x, List<string> y) {
        bool output = x.SequenceEqual(y);
        return output;
    }

    public int GetHashCode(List<string> obj)
    {
        int output = obj.Aggregate(13, (hash, y) => hash * 7 + y?.GetHashCode() ?? 0);
        return output;
    }
}

Использование:

List<Row> users = new List<Row>()
{
    new Row("John", "Doe", "10"),
    new Row("John", "Doe", "45"),
    new Row("Will","Smith", "26"),
    new Row("Will", "Smith", "52"),
    new Row("Julius", "Cesar", "23")
};

List<int> groupingColumnIndexes = new List<int>() { 0, 1 };

List<User> output = users
            .GroupBy(x =>
                 groupingColumnIndexes.Select(c => x.ColumnValues[c]).ToList(),
                 new StringColumnEqualityComparer()
            )
            .Select(x => new User
            {
                Name = string.Join(',', x.Key),
                Age = (int)x.Sum(a => int.Parse(a.ColumnValues[2])),
                LastName = string.Empty
            }).ToList();
0 голосов
/ 13 июня 2019

Вам нужно реализовать IEqualityComparer, чтобы ваши столбцы были GroupBy 'd:

class LambdaComparer<T> : IEqualityComparer<T>
{
    private readonly Func<T, T, bool> equals;
    private readonly Func<T, int> getHashCode;

    public LambdaComparer(Func<T, T, bool> equals, Func<T, int> getHashCode)
    {
        this.equals = equals;
        this.getHashCode = getHashCode;
    }

    public bool Equals(T x, T y) => equals(x, y);
    public int GetHashCode(T obj) => getHashCode(obj);
}

var output = users
    .GroupBy(
        x => groupingColumnIndexes.Select(i => x.ColumnValues[i]).ToArray(),
        new LambdaComparer<string[]>((a, b) => a.SequenceEqual(b), x => x.Aggregate(13, (hash, y) => hash * 7 + y?.GetHashCode() ?? 0))
    )
    .Select(g => new User
    {
        Name = g.Key[0],
        LastName = g.Key[1],
        Age = g.Sum(x => int.Parse(x.ColumnValues[2]))
    })
    .ToList();
...