开发者

Pivot an observable collection [duplicate]

This question already has answers here: Is it possible to Pivot data using LINQ? (7 answers) Closed 9 years ago.

I have records like this in observable collection

ID Department salary joingdate 
1    .NET   5000  04/08/2011
2    .NET   6000  04/07/2011
3    JAVA   7000  04/08/2011
4    JAVA   8000  04/07/2011
5    .NET   9000  04/06/2011

now I want new observable collection like开发者_开发技巧

Joingdate    .NET(Salary)  JAVA(Salary)
04/08/2011  5000        7000
04/07/2011  6000        8000
04/06/2011  9000        NULL

How would I get this type of observable collection?


Assuming you have follwing structure that resembles your structure:

public class Data1
{
    public int Id { get; set; }
    public String Dep { get; set; }
    public int Sal { get; set; }
    public String JoinDate { get; set; }
}

public class Data2
{
    public Data2()
    {
        Sal = new List<int>();
    }

    public List<int> Sal { get; set; }
    public String JoinDate { get; set; }

    public override string ToString()
    {
        return Sal.Aggregate(JoinDate, (current, s) => current + s.ToString());
    }
}

And following observable collections:

public class Data1List : ObservableCollection<Data1>
{
    public Data1List()
    {
        Add(new Data1{ Id = 1, Dep = ".NET", Sal = 5000, JoinDate = "04/08/2011"});
        Add(new Data1{ Id = 2, Dep = ".NET", Sal = 6000, JoinDate = "04/07/2011"});
        Add(new Data1{ Id = 3, Dep = "JAVA", Sal = 7000, JoinDate = "04/08/2011"});
        Add(new Data1{ Id = 4, Dep = "JAVA", Sal = 8000, JoinDate = "04/07/2011"});
        Add(new Data1{ Id = 5, Dep = ".NET", Sal = 9000, JoinDate = "04/06/2011"});
    }
}

public class Data2List : ObservableCollection<Data2>
{
}

You could try this code to solve your problem or at least get you on the way to a maybe better solution:

var l1 = new Data1List();
var l2 = new Data2List();

foreach (var items in l1.GroupBy(d => d.JoinDate))
{
    var d2 = new Data2 { JoinDate = items.Key };
    foreach (var item in items)
        d2.Sal.Add(item.Sal);
    l2.Add(d2);
}

I hope this helps you getting to a solution! :)


Some time ago I played around with pivoting and the ExpandoObject. This is certainly not production code.

public static dynamic pivot(IEnumerable<Employee> rows)
{
    IDictionary<string, Object> expando = new ExpandoObject();
    expando["Joindate"] = rows.First().Joindate;
    foreach (var row in rows)
    {
        expando[row.Department] = row.Salary;
    }
    return (dynamic)expando;
}

then in some method somewhere

var employees = new ObservableCollection<Employee>() { 
    new Employee() {ID=1, Department="NET", Salary=5000, Joindate=new DateTime(2011,04,08)},
    new Employee() {ID=2, Department="NET", Salary=6000, Joindate=new DateTime(2011,04,07)},
    new Employee() {ID=3, Department="JAVA", Salary=7000, Joindate=new DateTime(2011,04,08)},
    new Employee() {ID=4, Department="JAVA", Salary=8000, Joindate=new DateTime(2011,04,07)},
    new Employee() {ID=5, Department="NET", Salary=9000, Joindate=new DateTime(2011,04,06)}
};

var distinctDates = employees.Select(j => j.Joindate).Distinct().OrderByDescending(d => d);

var salaryByDepartmentAndJoindate = distinctDates.Select(d => pivot(employees.Where(jd => jd.Joindate == d)));

var result = new ObservableCollection<dynamic>(salaryByDepartmentAndJoindate);


Due to how my data is consumed, I have needed the results in a table format, not a nested object format. So the code below produces a pivot from a list of data. It is called like this:

    // generate a pivot table
    var pivot = linqQueryResults.Pivot(
        rowKey => rowKey.DepartmentName,
        columnKey => columnKey.JoiningDate,
        value => value.Sum(emp => emp.Salary),
        "Department",
        new Dictionary<string, Func<GetComplianceForClientCurriculums_Result, object>>()
            {
                {"DepartmentCode", extraRow => extraRow.DepartmentCode},
                {"DepartmentManager", extraRow => extraRow.DeptManager}
            }
    );

And the LINQ extension method looks like this:

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;

namespace MyApplication.Extensions 
{

    public static class LinqExtenions 
    {
        /// <summary>
        ///     Groups the elements of a sequence according to a specified firstKey selector 
        ///     function and rotates the unique values from the secondKey selector function into 
        ///     multiple values in the output, and performs aggregations. 
        /// </summary>
        /// <param name="source">The data source for the pivot</param>
        /// <param name="rowKeySelector">A function to derive the key for the rows</param>
        /// <param name="columnKeySelector">A function to derive the key for the columns</param>
        /// <param name="valueSelector">A function to calculate the contents of the intersection element. Usually this is an aggreation function</param>
        /// <param name="firstColumnName">The label to give the first column (row title)</param>
        /// <param name="additionalHeaderSelectors">An optional dictionary of additional rows to use as headers. Typically, this data should be consistent with the row selector since only the first match is taken.</param>
        /// <returns>A datatable pivoted from the IEnumerable source.</returns>
        /// <remarks>
        /// Based on concepts from this article: http://www.extensionmethod.net/Details.aspx?ID=147
        /// </remarks>
        public static DataTable Pivot<TSource, TRowKey, TColumnKey, TValue>(this IEnumerable<TSource> source, Func<TSource, TRowKey> rowKeySelector, Func<TSource, TColumnKey> columnKeySelector, Func<IEnumerable<TSource>, TValue> valueSelector, string firstColumnName = "", IDictionary<string, Func<TSource, object>> additionalHeaderSelectors = null)
        {
            var result = new DataTable();

            // determine what columns the datatable needs and build out it's schema
            result.Columns.Add(new DataColumn(firstColumnName));
            var columnNames = source.ToLookup(columnKeySelector);
            foreach (var columnName in columnNames)
            {
                var newColumn = new DataColumn(columnName.Key.ToString());
                result.Columns.Add(newColumn);
            }

            // if we have a 2nd header row, add it
            if (additionalHeaderSelectors != null)
            {
                foreach (var additionalHeaderSelector in additionalHeaderSelectors)
                {
                    var newRow = result.NewRow();

                    newRow[firstColumnName] = additionalHeaderSelector.Key;

                    foreach (var columnName in columnNames)
                    {
                        newRow[columnName.Key.ToString()] = additionalHeaderSelector.Value(columnName.FirstOrDefault());
                    }

                    result.Rows.Add(newRow);
                }
            }


            // build value rows
            var rows = source.ToLookup(rowKeySelector);
            foreach (var row in rows)
            {
                var newRow = result.NewRow();

                // put the key into the first column
                newRow[firstColumnName] = row.Key.ToString();

                // get the values for each additional column
                var columns = row.ToLookup(columnKeySelector);
                foreach (var column in columns) 
                {
                    newRow[column.Key.ToString()] = valueSelector(column);
                }

                result.Rows.Add(newRow);
            }

            return result;
        }
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜