Группировка и соединение строк в DataTable

258
25 марта 2018, 18:36

В общем, хочу написать метод, который на вход принимает: DataTable(заранее неизвестная структура),String[], где содержатся колонки по которым нужно сделать группировку, Делитель.

Задача в том, что бы выполнить группировку по полям, которые перечислены в массиве, далее внутри каждой группы сделать объединение строк, через делитель.

Т.е дана такая таблица:

Grp Val1 Val2

1 ; v1 ; d1

1 ; v2 ; d2

1 ; v3 ; d3

2 ; v1 ;d1

2 ; v2; d2

2 ; v3 ; d3

На выходе получится 2 строки:

1 ;v1 | v2 | v3; d1 | d2| d3

2 ;v1 | v2 | v3; d1 | d2| d3

При помощи циклов я представляю, как это сделать. Но можно ли это сделать LINQ запросом?

UPD

Ну и вкратце ответ на вопрос "Почему именно DataTable?"

DataTable берется, как результат парсинга Excel, структура которых заранее неизвестна.

Мой выбор пал на DataTable именно из-за возможности на ходу конструировать таблицу.

Answer 1

Вот решение для List<Dictionary<name, value>>. Возможно, оно обобщается до решения для DataTable:

Для начала, нам нужен класс, который сравнивает последовательности объектов поэлементно:

class SequenceComparer : IEqualityComparer<List<object>>
{
    public bool Equals(List<object> x, List<object> y) =>
        x.Zip(y, (o1, o2) => o1.Equals(o2)).All(v => v);
    public int GetHashCode(List<object> list) =>
        list.Aggregate(0, (v, obj) => v ^ obj.GetHashCode());
}

Имея такой класс, сравнение тривиально. В качестве ключа мы выбираем нужные значения, и используем только что определённый компаратор.

var data = new[]
{
    new Dictionary<string, object>()
        { ["Name"] = "Вася", ["Age"] = 25, ["JobTitle"] = "Программист" },
    new Dictionary<string, object>()
        { ["Name"] = "Вася", ["Age"] = 22, ["JobTitle"] = "Программист" },
    new Dictionary<string, object>()
        { ["Name"] = "Федя", ["Age"] = 54, ["JobTitle"] = "Директор" },
    new Dictionary<string, object>()
        { ["Name"] = "Аделаида", ["Age"] = 20, ["JobTitle"] = "Муза" },
    new Dictionary<string, object>()
        { ["Name"] = "Федя", ["Age"] = 2, ["JobTitle"] = "Хомячок" },
};
var columns = new[] { "Name", "JobTitle" };
var groups = data.GroupBy(entity => SelectValues(entity, columns), new SequenceComparer());

Получаем 4 группы.

Answer 2

Вроде сделал, но что-то не так изящно получилось, как хотелось=(

var newDt = dt.Clone();
        var columns = new[] { "F1" ,"F2"};
        var res = dt.AsEnumerable().GroupBy(x => KeySelector(x, columns), (key, rows) => ResultSelector(rows, columns, newDt, " | "), new SequenceComparer());
    static List<object> KeySelector(DataRow row, string[] columns)
    {
        var listObjcets = new List<object>();
        foreach (var column in columns)
        {
            listObjcets.Add(row[column]);
        }
        return listObjcets;
    }
    class SequenceComparer : IEqualityComparer<List<object>>
    {
        public bool Equals(List<object> x, List<object> y) =>
            x.Zip(y, (o1, o2) => o1.Equals(o2)).All(v => v);
        public int GetHashCode(List<object> list) =>
            list.Aggregate(0, (v, obj) => v ^ obj.GetHashCode());
    }
    static DataRow ResultSelector(IEnumerable<DataRow> rows, string[] columns, DataTable dt, string separator)
    {
        var newRow = dt.NewRow();
        foreach (DataRow row in rows)
        {
            for (var index = 0; index < row.ItemArray.Length; index++)
            {
                var item = row.ItemArray[index];
                if (columns.Contains(dt.Columns[index].ColumnName))
                {
                    newRow[index] = item;
                }
                else
                {
                    newRow[index] += DBNull.Value.Equals(newRow[index]) ? item.ToString() : separator + item;
                }
            }
        }
        return newRow;
    }
READ ALSO
Помогите исправить ошибку Process is terminated due to StackOverflowException

Помогите исправить ошибку Process is terminated due to StackOverflowException

Делаю лабораторную работу, задание:

251
Парсинг значения json с помощью Newtonsoft на с#

Парсинг значения json с помощью Newtonsoft на с#

Задача: получить значение "lastPriceProtected"

346
c# awesomium отобразить webview в webcontrol

c# awesomium отобразить webview в webcontrol

Здравствуйте, подскажите, пожалуйста, использую awesomium с помощью webview

208
c# парсинг файлов multipart form data

c# парсинг файлов multipart form data

клиент на андроиде передает multipart/form-data данные серверу на c# строковые данные извлекаются без проблем, а вот файлы разобрать не могу android использует...

255