简体   繁体   English

使用LINQ和匿名类型进行数据透视?

[英]Pivot with LINQ and anonymous type?

I've searched the other "linq pivot" questions and I can't quite seem to find an exact match for mine. 我搜索了其他“ linq枢纽”问题,但似乎找不到与我完全匹配的问题。 I need to do this with all anonymous types. 我需要对所有匿名类型执行此操作。 I'm trying to track a checking transaction with data like the following 我正在尝试使用以下数据跟踪支票交易

Check# - Step - Amount

100 - Book - 100

100 - Bank - 100

100 - Account - 100

101 - Book  - 75

101 - Bank  - 75

101 - Account  - NULL

The result I'm looking for, again, as an anonymous type is: 我再次作为匿名类型正在寻找的结果是:

Check # Book   - Bank   - Account 

100 - 100 - 100- 100

101 - 75 - 75 - NULL

I really can't tell if I need to do a grouping first or not (by check#). 我真的不知道是否需要先进行分组(通过check#)。 I need it to be anonymous because I will NOT know the names of the steps as shown here. 我需要使其匿名,因为我将不知道此处显示的步骤名称。 Sometimes there will 3 steps, other times there will be many more. 有时会有3个步骤,其他时候会有更多。

I've done something similar. 我做了类似的事情。 Anonymous types wouldn't work since I had to do the columns dynamically and anonymous types still must be known at compile-time . 匿名类型不起作用,因为我必须动态地进行列操作,并且仍然必须在编译时知道匿名类型。 The ExpandoObject , however, allows us to define properties at run-time . 但是, ExpandoObject允许我们在运行时定义属性。

I've done a quick console app as proof: 我已经做了一个快速的控制台应用程序作为证明:

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

class Program
{
    static void Main(string[] args)
    {
        List<Record> input = new List<Record>();
        input.Add(new Record { CheckNumber = 100, Step = "Book", Amount = 100 });
        input.Add(new Record { CheckNumber = 100, Step = "Bank", Amount = 100 });
        input.Add(new Record { CheckNumber = 100, Step = "Account", Amount = 100 });
        input.Add(new Record { CheckNumber = 101, Step = "Book", Amount = 75 });
        input.Add(new Record { CheckNumber = 101, Step = "Bank", Amount = 75 });
        List<ExpandoObject> results = GetPivotRows(input);

        //test
        for (int i = 0; i < results.Count; i++)
        {
            dynamic record = results[i];
            Console.WriteLine("{0} - {1} - {2} - {3}", record.CheckNumber, record.Book, record.Bank, record.Account);
        }
    }

    public static List<ExpandoObject> GetPivotRows(List<Record> input)
    {
        List<string> steps = input.Select(e => e.Step).Distinct().ToList();
        Dictionary<int, ExpandoObject> outputMap = new Dictionary<int,ExpandoObject>();
        for (int i = 0; i < input.Count; i++)
        {
            dynamic row;
            if(outputMap.ContainsKey(input[i].CheckNumber))
            {
                row = outputMap[input[i].CheckNumber];
            }
            else
            {
                row = new ExpandoObject();
                row.CheckNumber = input[i].CheckNumber;
                outputMap.Add(input[i].CheckNumber, row);

                // Here we're initializing all the possible "Step" columns
                for (int j = 0; j < steps.Count; j++)
                {
                    (row as IDictionary<string, object>)[steps[j]] = new Nullable<int>();
                }
            }

            (row as IDictionary<string, object>)[input[i].Step] = input[i].Amount;
        }

        return outputMap.Values.OrderBy(e => ((dynamic)e).CheckNumber).ToList();
    }
}

public class Record
{
    public int CheckNumber { get; set; }

    public string Step { get; set; }

    public decimal Amount { get; set; }
}

Output: 输出:

100 - 100 - 100- 100

101 - 75 - 75 - 

You can use reflection to check the actual properties created in the process. 您可以使用反射来检查在过程中创建的实际属性。

EDIT: Demystifying this a little bit - if I change that "test" loop in the main to: 编辑:神秘化一点-如果我将主菜单中的“测试”循环更改为:

for (int i = 0; i < results.Count; i++)
{
    Console.WriteLine(string.Join(" - ", results[i]));
}

I get: 我得到:

[CheckNumber, 100] - [Book, 100] - [Bank, 100] - [Account, 100]
[CheckNumber, 101] - [Book, 75] - [Bank, 75] - [Account, ]

ExpandoObject implements IDictionary<string, object> behind the scenes to store whatever it needs to yet at the same time implements IDynamicMetaObjectProvider and works with dynamic binding. ExpandoObject在后台实现IDictionary<string, object>以存储所需的内容,同时实现IDynamicMetaObjectProvider并与动态绑定一起使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM