views:

34

answers:

2

This is a segue to my previous question:

http://stackoverflow.com/questions/3141285/looking-for-a-better-way-to-sort-my-listt

Basically, I have a similar situation where I need to do a .GroupBy() on about 40 different fields.

The original code would have used a giant switch statement, but I'd like to know if there's a better way to this.

What I'd really like to do is something like:

// not sure what the GroupBy selector function def should be...
Dictionary<PortfolioMapping, Func<Holding, ???>> groupByMappings;

which I can use to group by like:

myPortfolioHoldings.GroupBy(groupByMaping[frmGroupBySelector.SelectedColumn]);

What would be the correct way to go about this?

A: 

Well, what I personally do is build up lambda expressions and then applying the final expression to the list.

I use some extension method to Linq.Expression which just wrap something like the following:

<System.Runtime.CompilerServices.Extension()> _
Public Function Compose(Of T)(ByVal first As Expressions.Expression(Of T), ByVal second As Expressions.Expression(Of T), ByVal merge As Func(Of Expressions.Expression, Expressions.Expression, Expressions.Expression)) As Expressions.Expression(Of T)

    '' build parameter map (from parameters of second to parameters of first)
    Dim map = first.Parameters.[Select](Function(f, i) New With {f, .s = second.Parameters(i)}).ToDictionary(Function(p) p.s, Function(p) p.f)

    '' replace parameters in the second lambda expression with parameters from the first
    Dim secondBody = ParameterRebinder.ReplaceParameters(map, second.Body)

    '' applycomposition of lambda expression bodies to parameters from the first expression 
        Return Expressions.Expression.Lambda(Of T)(merge(first.Body, secondBody), first.Parameters)
    End Function

<System.Runtime.CompilerServices.Extension()> _
Public Function [And](Of T)(ByVal first As Expressions.Expression(Of Func(Of T, Boolean)), ByVal second As Expressions.Expression(Of Func(Of T, Boolean))) As Expressions.Expression(Of Func(Of T, Boolean))
    Return first.Compose(second, AddressOf Expressions.Expression.And)
End Function

And then you can build queries like this:

Dim MyQuery as Linq.Expressions.Expression(Of System.Func(Of MyListDataType, Boolean))
Dim MyGroupingExpression as Linq.Expressions.Expression(Of System.Func(Of MyListDataType, Boolean))

Dim MyQuery = Function(x) x.SomeValue = SomeExpectedValue
Select case SomeOtherVariable
    Case Option1
        Dim MyGroupingExpression = <blah>
    Case Option2
        Dim MyGroupingExpression = <blah>
End Select

Dim Results = MyList.Where(MyQuery.And(GroupingExpression))

Is that what you're after?

Basiclife
+1  A: 

You should be able to group by object:

Dictionary<PortfolioMapping, Func<Holding, object>> groupByMappings;

This will work as long as your mapping functions return either:

  • Instances of built-in types (such as number or strings)
  • Objects that implement IEquatable<T>
  • Objects that implement Equals and GetHashCode correctly
Tim Robinson
code4life