Condividi tramite


VB.NET Eseguire Pivot su DataTable (it-IT)


Introduzione

In questo articolo vediamo un modo rapido per eseguire l'operazione di pivot su un oggetto di tipo DataTable. Tale oggetto, costituito da una struttura realizzata da colonne e righe, rappresenta un insieme di dati in memoria. L'operazione di pivoting prevede che, a partire da una data matrice, venga realizzato lo scambio tra le righe e le colonne che la costituiscono, sulla base dei valori di riga presenti su una colonna selezionata.

Un esempio di DataTable

Consideriamo una tabella costituita dalle seguenti informazioni (colonne): Nome, Età, Peso, Q.I., e valorizziamola con alcuni dati (righe):

Dim dt As New  DataTable("Data")
dt.Columns.Add("Name")
dt.Columns.Add("Age")
dt.Columns.Add("Weight")
dt.Columns.Add("IQ")
 
dt.Rows.Add(New Object() {"Alice", 20, 45, 130})
dt.Rows.Add(New Object() {"Bob", 52, 70, 125})
dt.Rows.Add(New Object() {"Sam", 35, 85, 106})
dt.Rows.Add(New Object() {"Jane", 41, 48, 129})

Si è generata cioè una DataTable, di nome Data, costituita dalle quattro colonne menzionate sopra, e sono state aggiunte altrettante righe, corrispondenti a individui ipotetici. Utilizzando la DataTable dt come DataSource di una DataGridView, otterremo un'esposizione conforme alla definizione:

Eseguire il pivoting rispetto ad una colonna

Supponiamo ora di voler eseguire il pivoting della tabella rispetto alla colonna Name. Desideriamo, cioè, che il contenuto della colonna Name su tutte le righe (Alice, Bob, Sam, Jane) venga trasformato in altrettante colonne, mentre le proprietà Age, Weight, IQ diventeranno le righe da esporre ciascuna nella colonna di appartenenza.

Svolgiamo qui l'operazione in due step: nel primo, scorreremo tutte le righe della tabella originaria, e da essa estrarremo l'elemento di pivoting (nel nostro caso, Name), per andare a generare su una seconda tabella le rispettive colonne. In un secondo passaggio, scorreremo ogni colonna della DataTable sorgente che non sia l'elemento di pivoting, e per ciascuna di esse scorreremo le righe, estraendone i valori e popolando, ad ogni ciclo, righe costituite dai valori di ciascun record sulla colonna analizzata in quel momento. Possiamo cioè scrivere una funzione di questo tipo:

Private Function  PivotTable(ByVal  dt As  DataTable, ByVal  ColNum As  Integer) As DataTable
    Dim dp As New  DataTable("Pivoted")
    dp.Columns.Add("Property")
    For Each  row As  DataRow In  dt.Rows
        dp.Columns.Add(row.Item(ColNum).ToString)
    Next
 
    Dim IColumns As IEnumerable(Of DataColumn) = From c As DataColumn In dt.Columns
                                                 Where c.Ordinal <> ColNum
 
    For Each  col As  DataColumn In  IColumns
        Dim tr(dt.Rows.Count) As Object
        tr(0) = col.ColumnName
 
        Dim i As Integer  = 1
        For Each  row As  DataRow In  dt.Rows
            tr(i) = row.Item(col.Ordinal)
            i += 1
        Next
 
        dp.Rows.Add(tr)
    Next
 
    Return dp
End Function

Si noti che la funzione accetta due parametri: il primo è riferito alla DataTable di origine, mentre il secondo è la posizione cardinale della colonna rappresentante l'elemento di pivoting. Supponendo di voler popolare due DataGridView, rispettivamente con i dati di origine e con quelli pivotati sulla colonna Name, potremo rifarci a questo snippet:

Public Class  Form1
 
    Private Sub  Form1_Load(sender As  Object, e As EventArgs) Handles MyBase.Load
        Dim dt As New  DataTable("Data")
        dt.Columns.Add("Name")
        dt.Columns.Add("Age")
        dt.Columns.Add("Weight")
        dt.Columns.Add("IQ")
 
        dt.Rows.Add(New Object() {"Alice", 20, 45, 130})
        dt.Rows.Add(New Object() {"Bob", 52, 70, 125})
        dt.Rows.Add(New Object() {"Sam", 35, 85, 106})
        dt.Rows.Add(New Object() {"Jane", 41, 48, 129})
 
        DGV_Source.DataSource = dt
        DGV_Dest.DataSource = PivotTable(dt, 0)
    End Sub
 
    Private Function  PivotTable(ByVal  dt As  DataTable, ByVal  ColNum As  Integer) As DataTable
        Dim dp As New  DataTable("Pivoted")
        dp.Columns.Add("Property")
        For Each  row As  DataRow In  dt.Rows
            dp.Columns.Add(row.Item(ColNum).ToString)
        Next
 
        Dim IColumns As IEnumerable(Of DataColumn) = From c As DataColumn In dt.Columns
                                                     Where c.Ordinal <> ColNum
 
        For Each  col As  DataColumn In  IColumns
            Dim tr(dt.Rows.Count) As Object
            tr(0) = col.ColumnName
 
            Dim i As Integer  = 1
            For Each  row As  DataRow In  dt.Rows
                tr(i) = row.Item(col.Ordinal)
                i += 1
            Next
 
            dp.Rows.Add(tr)
        Next
 
        Return dp
    End Function
End Class

Nell'esempio si presuppone ovviamente di avere due DataGridView di nome DGV_Source e DGV_Dest, e che il Form che le contiene abbia nome Form1.
Il risultato dell'esecuzione dello snippet è quello di figura: