Home > Enterprise >  Pivot List of Object C# MVC Solution
Pivot List of Object C# MVC Solution

Time:09-28

In my controller I am returning a db query result (list of objects) defined by my model. I'm displaying this in the view. For example:

prop1 prop2 prop3 prop4 prop5
AAA 1123 400 35% $600
BBB 3444 23 45% $235
CCC 5000 55 15% $555
DDD 2500 264 70% $243

I would like to pivot this result to look like below:

prop1 AAA BBB CCC DDD
prop2 1123 3444 5000 2500
prop3 400 23 55 264
prop4 35% 45% 15% 70%
prop5 $600 $235 $555 $243

What's the best way to achieve this? I had initially pivoted in the SQL query and had a model representing the bottom result , however it became difficult to manage as I had frequent changes and requests to add new fields calculated from other columns etc.

CodePudding user response:

See below for an example implementation extension method below that uses reflection to pivot an IEnumerable<T>.

Example Usage Pivot()

// Test Type
public record MyObj(string Prop1, int Prop2, int Prop3);

// test data
var list = new List<MyObj> {
        new MyObj("AAA", 1123, 400),
        new MyObj("BBB", 2123, 500),
        new MyObj("CCC", 3123, 600) 
    };

Console.Write(list.Pivot());

Output

Example Usage Pivot() w/ ColumnSelector

Example 1

Console.Write(list.Pivot( pivotOn: (f)=>f.Prop1));

enter image description here

Example 2

Console.Write(list.Pivot( pivotOn: (f)=>f.Prop2));

enter image description here

Implementation

public static class PivotExtension
{
    public static IEnumerable<ExpandoObject> Pivot<T>(this IEnumerable<T> objs)
    {
        var objArray = objs.ToArray();
        var properties = typeof(T).GetProperties();
        foreach (var property in properties)
        {
            var obj = new ExpandoObject();
            var objDic = (IDictionary<string, object>)obj;
            objDic.Add("Name", property.Name);
            for (int i = 1; i < objs.Count(); i  )
                objDic.Add($"Row_{i.ToString()}", property.GetValue(objArray[i]));
                
            yield return obj;
        }
    }
    public static IEnumerable<ExpandoObject> Pivot<T, TValue>(this IEnumerable<T> objs, Expression<Func<T, TValue>> pivotOn)
    {
        var objArray = objs.ToList();
        var properties = typeof(T).GetProperties();
        var pivotProperty = pivotOn.GetProperty();
        
        //header
        var cnt = 1;
        var pivotHeader = new Dictionary<int, string>{{0, pivotProperty.Name}};
        objArray.ForEach(r=> pivotHeader.Add( cnt  , pivotProperty.GetValue(r).ToString()));
        
        //rotate
        foreach (var property in properties.Where(r=> !pivotHeader.ContainsValue(r.Name)))
        {
            var obj = new ExpandoObject();
            var objDic = (IDictionary<string, object>)obj;
            objDic.Add(pivotHeader[0], property.Name);
            for (int i = 0; i < objs.Count(); i  )
                objDic.Add($"{pivotHeader[i 1]}", property.GetValue(objArray[i]));

            yield return obj;
        }
    }
    
    public static PropertyInfo GetProperty<T, TValue>(this Expression<Func<T, TValue>> expression)
    {
        return GetProperty(expression.Body);
    }
    
    private static PropertyInfo GetProperty(Expression body)
    {
        MemberExpression memberExpression;
        switch (body.NodeType)
        {
            case ExpressionType.Convert:
                memberExpression = (MemberExpression)((UnaryExpression)body).Operand;
                break;
            case ExpressionType.MemberAccess:
                memberExpression = (MemberExpression)body;
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }

        return (PropertyInfo)memberExpression.Member;
    }
}
  • Related