Включить больше строк в сводную - PullRequest
0 голосов
/ 22 октября 2018

Я использую метод расширения в приведенной ниже ссылке для поворота моих данных: https://techbrij.com/pivot-c-array-datatable-convert-column-to-row-linq

Я включаю код из ссылки на тот случай, если кто-нибудь найдет этот вопрос в будущем и ссылка мертва:

public static DataTable ToPivotTable<T, TColumn, TRow, TData>(
    this IEnumerable<T> source,
    Func<T, TColumn> columnSelector,
    Expression<Func<T, TRow>> rowSelector,
    Func<IEnumerable<T>, TData> dataSelector)
        {
            DataTable table = new DataTable();
            var rowName = ((MemberExpression)rowSelector.Body).Member.Name;
            table.Columns.Add(new DataColumn(rowName));
            var columns = source.Select(columnSelector).Distinct();

            foreach (var column in columns)
                table.Columns.Add(new DataColumn(column.ToString()));

            var rows = source.GroupBy(rowSelector.Compile())
                             .Select(rowGroup => new
                             {
                                 Key = rowGroup.Key,
                                 Values = columns.GroupJoin(
                                     rowGroup,
                                     c => c,
                                     r => columnSelector(r),
                                     (c, columnGroup) => dataSelector(columnGroup))
                             });

            foreach (var row in rows)
            {
                var dataRow = table.NewRow();
                var items = row.Values.Cast<object>().ToList();
                items.Insert(0, row.Key);
                dataRow.ItemArray = items.ToArray();
                table.Rows.Add(dataRow);
            }

            return table;
        }

Обращаясь к примеру в ссылке, вы получаете сводные данные, такие как:

var pivotTable = data.ToPivotTable(
              item => item.Year, 
              item => item.Product,  
              items => items.Any() ? items.Sum(x=>x.Sales) : 0);

Мой вопрос заключается в том, как я могу включить больше строк в этот запрос для возврата, например, ProductCode также .. item => new {item.Product, item.ProductCode} не работает ..


============== РЕДАКТИРОВАТЬ / 23 ОКТЯБРЯ 2018 ==============


Предположим, мои данные таковы;

enter image description here

С помощью вышеупомянутого кода мне удается это сделать; enter image description here

Чего я хочу добиться, так этоthis (дополнительно: STOCKID или любые другие столбцы); enter image description here

Ответы [ 2 ]

0 голосов
/ 24 октября 2018

Анонимные типы нельзя передавать как общие параметры.Попробуйте определить свой сводный ключ как структуру:

public struct PivotKey
{
    public string Product;
    public int ProductCode; // assuming your product codes are integers
}

Таким образом, вы можете воспользоваться преимуществами реализации методов struct Equals и GetHashCode по умолчанию с точки зрения равенства и хэш-кода всех полей.

Затем определите rowSelector, как показано ниже:

item => new PivotKey { Product = item.Product, ProductCode = item.ProductCode}
0 голосов
/ 24 октября 2018

Пример: https://dotnetfiddle.net/mXr9sh

Кажется, проблема заключается в получении имен строк из выражения, поскольку он предназначен только для обработки одной строки.Это можно исправить с помощью этой функции:

public static IEnumerable<string> GetMemberNames<T1, T2>(Expression<Func<T1, T2>> expression)
{
    var memberExpression = expression.Body as MemberExpression;
    if (memberExpression != null) 
    {
        return new[]{ memberExpression.Member.Name };
    }
    var memberInitExpression = expression.Body as MemberInitExpression;
    if (memberInitExpression != null)
    {
        return memberInitExpression.Bindings.Select(x => x.Member.Name);
    }
    var newExpression = expression.Body as NewExpression;
    if (newExpression != null)
    {
        return newExpression.Arguments.Select(x => (x as MemberExpression).Member.Name);
    }

    throw new ArgumentException("expression"); //use: `nameof(expression)` if C#6 or above
}

Когда у вас есть эта функция, вы можете заменить эти строки:

var rowName = ((MemberExpression)rowSelector.Body).Member.Name;
table.Columns.Add(new DataColumn(rowName));

на:

var rowNames = GetMemberNames(rowSelector);
rowNames.ToList().ForEach(x => table.Columns.Add(new DataColumn(x)));

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


Результирующий DataTable:

(отображается в формате JSON)

[
  {
    "StockId": "{ StockId = 65, Name = Milk }",
    "Name": "3",
    "Branch 1": "1",
    "Branch 2": "0",
    "Central Branch": null
  },
  {
    "StockId": "{ StockId = 67, Name = Coffee }",
    "Name": "0",
    "Branch 1": "0",
    "Branch 2": "22",
    "Central Branch": null
  }
]

Полный кодЛистинг

using System;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using Newtonsoft.Json; //just for displaying output

public class Program 
{
    public static void Main()
    {
        var data = new[] { 
            new { StockId = 65, Name = "Milk", Branch = 23, BranchName = "Branch 1", Stock = 3 },
            new { StockId = 65, Name = "Milk", Branch = 24, BranchName = "Branch 2", Stock = 1 },
            new { StockId = 67, Name = "Coffee", Branch = 22, BranchName = "Central Branch", Stock = 22 }
        };

        var pivotTable = data.ToPivotTable(
            item => item.BranchName, 
            item => new {item.StockId, item.Name},  
            items => items.Any() ? items.Sum(x=>x.Stock) : 0);

        //easy way to view our pivotTable if using linqPad or similar
        //Console.WriteLine(pivotTable);
        //if not using linqPad, convert to JSON for easy display
        Console.WriteLine(JsonConvert.SerializeObject(pivotTable, Formatting.Indented));
    }
}   

public static class PivotExtensions
{
    public static DataTable ToPivotTable<T, TColumn, TRow, TData>(
        this IEnumerable<T> source,
        Func<T, TColumn> columnSelector,
        Expression<Func<T, TRow>> rowSelector,
        Func<IEnumerable<T>, TData> dataSelector)
    {
        DataTable table = new DataTable();
        //foreach (var row in rowSelector()
        var rowNames = GetMemberNames(rowSelector);
        rowNames.ToList().ForEach(x => table.Columns.Add(new DataColumn(x)));
        var columns = source.Select(columnSelector).Distinct();

        foreach (var column in columns)
            table.Columns.Add(new DataColumn(column.ToString()));

        var rows = source.GroupBy(rowSelector.Compile())
            .Select(rowGroup => new
                    {
                        Key = rowGroup.Key,
                        Values = columns.GroupJoin(
                            rowGroup,
                            c => c,
                            r => columnSelector(r),
                            (c, columnGroup) => dataSelector(columnGroup))
                    });

        foreach (var row in rows)
        {
            var dataRow = table.NewRow();
            var items = row.Values.Cast<object>().ToList();
            items.Insert(0, row.Key);
            dataRow.ItemArray = items.ToArray();
            table.Rows.Add(dataRow);
        }

        return table;
    }
    public static IEnumerable<string> GetMemberNames<T1, T2>(Expression<Func<T1, T2>> expression)
    {
        var memberExpression = expression.Body as MemberExpression;
        if (memberExpression != null) 
        {
            return new[]{ memberExpression.Member.Name };
        }
        var memberInitExpression = expression.Body as MemberInitExpression;
        if (memberInitExpression != null)
        {
            return memberInitExpression.Bindings.Select(x => x.Member.Name);
        }
        var newExpression = expression.Body as NewExpression;
        if (newExpression != null)
        {
            return newExpression.Arguments.Select(x => (x as MemberExpression).Member.Name);
        }

        throw new ArgumentException("expression"); //use: `nameof(expression)` if C#6 or above
    }

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