En un producto en el tengo la oportunidad de apoyar se necesitaba desplegar unas gráficas con la siguiente particularidad. Del data source solo se mostrarían los 5 valores mayores y el resto se acumularían y se desplegarían como un agregado.
Los datos en la aplicación se encuentran en un DataTable y en vez de ordenar con DataViews y sumar y seleccionar de la manera tradicional se me ocurrió utilizar LINQ para llevar a cabo la tarea. Combinando así tecnología de dos generaciones.
Anexo la clase con comentarios, no es una solución completamente genérica pero funciona para este producto y es reutilizable en toda la solución.
public static class DataAcummulator { /// <summary> /// Generates a DataTable with only the top values and the /// rest are aggregated as a single row /// </summary> /// <param name="source">Original data source. Will not be modified</param> /// <param name="topValues">number of top values to include</param> /// <param name="ValueColumn">column to be used as selection criteria and to be accumulated</param> /// <param name="percentage">does the datatable contains a percentage column?</param> /// <returns></returns> public static DataTable GetAcummulatedData(DataTable source, int topValues, string ValueColumn,bool percentage) { //primero ordenamos los datos del datatable de manera ascendente //notese que sabemos que la columna va a ser de tipo Decimal var query = from data in source.AsEnumerable() orderby data.Field<Decimal>(ValueColumn) descending select data; //de la coleccion ordenada tomamos los primeros n valores var firstValues = query.Take(topValues); //calculamos el total de una columna que siempre aparece en esta aplicacion //ok, esto está hardcodeado pero dentro del scope funciona perfecto var restRecordsTotalValue = query.Skip(topValues).Sum(c => c.Field<int>("Quotes")); //notese que Sum pudiera cambiarse por algo más complejo en caso de requerise var restRecordsTotalPercentage = query.Skip(topValues).Sum(c => c.Field<Decimal>(ValueColumn)); //clonamos la estructura de la tabla, recuerden que no vamos a modificar el source DataTable filteredTable = source.Clone(); //se usa import row porque los otros rows ya están attacheados al source DataTable foreach (var row in firstValues) { filteredTable.ImportRow(row); } //creamos el row nuevo que va a contener los valores acumulados //de nuevo hay hard code de cosas que sabemos que son ciertas en este proyecto var othersRow = filteredTable.NewRow(); othersRow["Value"] = "Others"; othersRow["Quotes"] = restRecordsTotalValue; if (percentage) { othersRow["Percentage"] = restRecordsTotalPercentage.ToString("p"); } filteredTable.Rows.Add(othersRow); //voila! return filteredTable; } }