2016-08-04 2 views
1

Есть несколько похожих звуковых сообщений, но никто из них не делает именно то, что я хочу.Как динамически GroupBy с использованием Linq

Итак, представьте себе, что у меня есть следующая структура данных (упрощенный для данного примера LINQPad)

public class Row 
{ 
    public List<string> Columns { get; set; } 
} 

public List<Row> Data 
     => new List<Row> 
     { 
       new Row { Columns = new List<string>{ "A","C","Field3"}}, 
       new Row { Columns = new List<string>{ "A","D","Field3"}}, 
       new Row { Columns = new List<string>{ "A","C","Field3"}}, 
       new Row { Columns = new List<string>{ "B","D","Field3"}}, 
       new Row { Columns = new List<string>{ "B","C","Field3"}}, 
       new Row { Columns = new List<string>{ "B","D","Field3"}}, 
     }; 

для свойства «Data», то пользователь будет сказать мне, какой столбец числительные в GroupBy; они могут сказать «не группировать ничего», или они могут сказать «группа по столбцу [1]» или «группа по колонке [0] и столбец [1]».

Если я хочу, чтобы сгруппировать по одной колонке, я могу использовать:

var groups = Data.GroupBy(d => d.Columns[i]); 

И если я хочу, чтобы сгруппировать по 2 колонки, я могу использовать:

var groups = Data.GroupBy(d => new { A = d.Columns[i1], B = d.Columns[i2] }); 

Однако число столбцов - переменная (ноль -> много); Данные могут содержать сотни столбцов, и пользователю может понадобиться десятки столбцов GroupBy.

Итак, вопрос в том, как я могу создать этот GroupBy во время выполнения (динамически)?

Благодаря

Griff

+0

Вы могли бы как-то построить Lambda Expressions [очень похоже на этот ответ на очень похожий вопрос о динамическом OrderBy] (http://stackoverflow.com/a/233505/424129). –

ответ

2

С этим Структура данных, о которой вы просите, относительно проста.

Начало путем реализации пользовательских IEqualityComparer<IEnumerable<string>>:

public class ColumnEqualityComparer : EqualityComparer<IEnumerable<string>> 
{ 
    public static readonly ColumnEqualityComparer Instance = new ColumnEqualityComparer(); 
    private ColumnEqualityComparer() { } 
    public override int GetHashCode(IEnumerable<string> obj) 
    { 
     if (obj == null) return 0; 
     // You can implement better hash function 
     int hashCode = 0; 
     foreach (var item in obj) 
      hashCode ^= item != null ? item.GetHashCode() : 0; 
     return hashCode; 
    } 
    public override bool Equals(IEnumerable<string> x, IEnumerable<string> y) 
    { 
     if (x == y) return true; 
     if (x == null || y == null) return false; 
     return x.SequenceEqual(y); 
    } 
} 

Теперь вы можете иметь метод, как это:

public IEnumerable<IGrouping<IEnumerable<string>, Row>> GroupData(IEnumerable<int> columnIndexes = null) 
{ 
    if (columnIndexes == null) columnIndexes = Enumerable.Empty<int>(); 
    return Data.GroupBy(r => columnIndexes.Select(c => r.Columns[c]), ColumnEqualityComparer.Instance); 
} 

Примечание тип группировки Key является IEnumerable<string> и содержит выбранные значения строк, заданных опциями columnIndexes, поэтому нам понадобился пользовательский сопоставитель сравнений (в противном случае они будут сравниваться по ссылке, что не приведет к требуемому поведению).

Например, в группе по столбцам 0 и 2 вы могли бы использовать что-то вроде этого:

var result = GroupData(new [] { 0, 2 }); 

Passing null или пустой columnIndexes будет эффективно производить одну группу, то есть не группировки.

0

вы можете использовать рекурсивную функцию для создания динамических lambdaExpression. но вы должны определить столбцы HardCode в функции.

Смежные вопросы