Click here to Skip to main content
Licence CPOL
First Posted 6 Dec 2009
Views 48,825
Downloads 1,952
Bookmarked 161 times

Pivoting DataTable Simplified

By Anurag Gandhi | 19 Mar 2010
A class to pivot a DataTable with various aggregate functions.
Prize winner in Competition "Best ASP.NET article of December 2009"

1
1 vote, 1.7%
2
1 vote, 1.7%
3
7 votes, 11.9%
4
50 votes, 84.7%
5
4.90/5 - 59 votes
2 removed
μ 4.84, σa 0.96 [?]

Introduction

Displaying data in tabular form is an essential part of any application nowadays. But sometimes you need to display a huge amount of data in terms of number of rows. It becomes very difficult to analyse if the number of rows is huge. In such cases, you may wish to summarize your data in the other formats like charts, graphs, groups, pivots, etc. This articles presents a simplified way to pivot your data with an appropriate aggregate function so that you can enhance your reports easily.

Below is a screenshot of pivoted data in a GridView:

2ndPivot.JPG

How it Works

To simplify the scenario, I have divided the result table into three areas: RowField, DataField, and ColumnFields. Apart from the area, the Pivot class provides you the option to bind your data based on some aggregate functions. The various aggregate options available are:

  • Count: Returns the count of matching data
  • Sum: Returns the sum of matching data (to get the sum, the type of the DataField must be convertible to decimal type)
  • First: Returns the first occurrence of matching data
  • Last: Returns the last occurrence of matching data
  • Average: Returns the average of matching data (to get the average, the type of the DataField must be convertible to decimal type)
  • Max: Returns the maximum value from the matching data
  • Min: Returns the minimum value from the matching data
  • Exists: Returns "true" if there is any matching data, else "false"

The code mainly contains a class named "Pivot" that takes the DataTable in the constructor. ColumnFields takes as a string array parameter which allows you to pivot data on more than one column. It contains a function called PivotData() which actually pivots your data.

public DataTable PivotData(string RowField, string DataField, 
       AggregateFunction Aggregate, params string[] ColumnFields)
{
    DataTable dt = new DataTable();
    string Separator = ".";
    var RowList = (from x in _SourceTable.AsEnumerable() 
        select new { Name = x.Field<object>(RowField) }).Distinct();
    var ColList = (from x in _SourceTable.AsEnumerable() 
                   select new { Name = ColumnFields.Select(n => x.Field<object>(n))
                       .Aggregate((a, b) => a += Separator + b.ToString()) })
                       .Distinct()
                       .OrderBy(m => m.Name);

    dt.Columns.Add(RowField);
    foreach (var col in ColList)
    {
        dt.Columns.Add(col.Name.ToString());
    }

    foreach (var RowName in RowList)
    {
        DataRow row = dt.NewRow();
        row[RowField] = RowName.Name.ToString();
        foreach (var col in ColList)
        {
            string strFilter = RowField + " = '" + RowName.Name + "'";
            string[] strColValues = 
              col.Name.ToString().Split(Separator.ToCharArray(), 
                                        StringSplitOptions.None);
            for (int i = 0; i < ColumnFields.Length; i++)
                strFilter += " and " + ColumnFields[i] + 
                             " = '" + strColValues[i] + "'";
            row[col.Name.ToString()] = GetData(strFilter, DataField, Aggregate);
        }
        dt.Rows.Add(row);
    }
    return dt;
}

First of all, the function determines the number of rows by getting the distinct values in RowList, and the number of columns by getting the distinct values in ColList. Then, the columns are created. It then iterates through each row and gets the matching values to the corresponding cell based on the aggregate function provided. To retrieve the matching value, the GetData() function is called.

private object GetData(string Filter, string DataField, AggregateFunction Aggregate)
{
    try
    {
        DataRow[] FilteredRows = _SourceTable.Select(Filter);
        object[] objList = 
         FilteredRows.Select(x => x.Field<object>(DataField)).ToArray();

        switch (Aggregate)
        {
            case AggregateFunction.Average:
                return GetAverage(objList);
            case AggregateFunction.Count:
                return objList.Count();
            case AggregateFunction.Exists:
                return (objList.Count() == 0) ? "False" : "True";
            case AggregateFunction.First:
                return GetFirst(objList);
            case AggregateFunction.Last:
                return GetLast(objList);
            case AggregateFunction.Max:
                return GetMax(objList);
            case AggregateFunction.Min:
                return GetMin(objList);
            case AggregateFunction.Sum:
                return GetSum(objList);
            default:
                return null;
        }
    }
    catch (Exception ex)
    {
        return "#Error";
    }
    return null;
}

This function first filters out the matching RowField and ColumnFields data in the DataRow[] array and then applies the aggregate function on it.

Using the Code

Using the code is simple. Create an instance of the Pivot class and then call the PivotData method with the required parameters. The PivotData() method returns the DataTable which can directly be used as the DataSource of the GridView.

DataTable dt = ExcelLayer.GetDataTable("_Data\\DataForPivot.xls", "Sheet1$");
Pivot pvt = new Pivot(dt);

grdPivot.DataSource = pvt.PivotData("Designation", "CTC", 
   AggregateFunction.Max, "Company", "Department", "Year");
grdPivot.DataBind();

The database used as a sample is an Excel sheet and is present in the "_Data" folder of the root folder of the sample application.

Merge GridView Header Cells

The MergeHeader function is created to merge the header cells to provide a simplified look.

private void MergeHeader(GridView gv, GridViewRow row, int PivotLevel)
{
    for (int iCount = 1; iCount <= PivotLevel; iCount++)
    {
        GridViewRow oGridViewRow = new GridViewRow(0, 0, 
          DataControlRowType.Header, DataControlRowState.Insert);
        var Header = (row.Cells.Cast<tablecell>()
            .Select(x => GetHeaderText(x.Text, iCount, PivotLevel)))
            .GroupBy(x => x);

        foreach (var v in Header)
        {
            TableHeaderCell cell = new TableHeaderCell();
            cell.Text = v.Key.Substring(v.Key.LastIndexOf(_Separator) + 1);
            cell.ColumnSpan = v.Count();
            oGridViewRow.Cells.Add(cell);
        }
        gv.Controls[0].Controls.AddAt(row.RowIndex, oGridViewRow);
    }
    row.Visible = false;
}

The function creates a new row for each pivot level and merges accordingly. PivotLevel here is the number of columns on which the pivot is done.

Header gets all the column values in an array, groups the repeated values returned by the GetHeaderText() function, sets the ColumnSpan property of the newly created cell according to the number of repeated HeaderText, and then adds the cell to the GridViewRow. Finally, add the GridViewRow to the GridView.

The GetHeaderText() function returns the header text based on the PivotLevel.

For example, suppose a pivot is done on three ColumnFields, namely, Company, Department, and Year. The result header of the GridView will initially have a header like Company.Department.Year for a PivotLevel 1. GetHeaderText() will return Company. For a PivotLevel 2, GetHeaderText() will return Company.Department. For a PivotLevel 3, GetHeaderText() will return Company.Department.Year, and so on...

Below is the screenshot of the GridView containing the third level pivoted data:

3rdPivot.JPG

Points of Interest

Along with pivoting the DataTable, the code will also help you to merge the header cells in the desired format in GridView. For beginners, the ExcelLayer.GetDataTable() method will be a sample to get the data from the Excel Sheet.

You may also wish to consider the following link to pivot a DataTable: http://www.codeproject.com/KB/recipes/CsharpPivotTable.aspx.

Future Consideration

Currently, the code can pivot data only for a DataTable. The code will be enhanced to pivot any object derived from an IListSource or ICollection.

History

  • First version release: December 09, 2009.
  • VB.NET source and demo added: March 19, 2010.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Anurag Gandhi

Software Developer (Senior)
Infosys Technologies Limited
India India

Member

Follow on Twitter Follow on Twitter
Anurag Gandhi is working on software design/development since last many years and he loves programming very much.
He is extensively Involved in Asp.Net web application development. The Language of his choice are C#.Net, Asp.Net, Asp, C, C++, etc. He works with MS Sql Server database as well.
He is active in programming communities and loves to share the knowledge with other developers whenever he gets the opportunity.
He is a passionate chess player as well.
 
He can be contacted at: soft.gandhi@gmail.com

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralMy vote of 5 Pinmembermanoj kumar choubey1:03 7 Feb '12  
GeneralMy vote of 5 PinmemberAnniKiran23:00 21 Sep '11  
GeneralRe: My vote of 5 PinmemberAnurag Gandhi21:39 10 Oct '11  
GeneralMy vote of 5 Pinmemberaaroncampf9:43 19 Jul '11  
GeneralRe: My vote of 5 PinmemberAnurag Gandhi20:24 21 Sep '11  
GeneralMy vote of 5 Pinmemberenexooone0:51 14 Jul '11  
GeneralRe: My vote of 5 PinmemberAnurag Gandhi20:24 21 Sep '11  
GeneralVS2005 users PinmemberF[]X0:03 6 Jun '11  
GeneralRe: VS2005 users PinmemberAnurag Gandhi0:46 15 Jun '11  
GeneralMy vote of 2 Pinmemberrutstyle6:00 19 Mar '11  
GeneralRe: My vote of 2 PinmemberAnurag Gandhi23:15 16 May '11  
QuestionMore than 1 line level Pinmemberastro__pc7:16 11 Jan '11  
AnswerRe: More than 1 line level PinmemberAnurag Gandhi8:48 11 Jan '11  
GeneralRe: More than 1 line level Pinmemberastro__pc0:43 12 Jan '11  
GeneralRe: More than 1 line level PinmemberAnurag Gandhi9:08 12 Jan '11  
GeneralRe: More than 1 line level PinmemberAnurag Gandhi23:05 16 Jan '11  
AnswerRe: More than 1 line level Pinmemberastro__pc23:53 17 Jan '11  
GeneralMy vote of 5 Pinmemberzaveriamit2:20 12 Nov '10  
GeneralRe: My vote of 5 PinmemberAnurag Gandhi9:23 22 Dec '10  
GeneralMy vote of 5 PinmemberKanasz Robert1:30 12 Nov '10  
GeneralRe: My vote of 5 PinmemberAnurag Gandhi9:23 22 Dec '10  
GeneralMy vote of 5 PinmemberEric Xue (brokensnow)14:49 6 Sep '10  
GeneralRe: My vote of 5 PinmemberAnurag Gandhi5:26 27 Sep '10  
GeneralMy vote of 5 Pinmemberthatraja1:15 3 Sep '10  
GeneralRe: My vote of 5 PinmemberAnurag Gandhi5:27 27 Sep '10  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web03 | 2.5.120209.1 | Last Updated 19 Mar 2010
Article Copyright 2009 by Anurag Gandhi
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid