Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Execute a custom operation on each item of a List<T>

Tags:

c#

list

generics

I would like to do a custom operation on a list's members and be able to specify on which property I would perform it but I'm having a hard time finding the correct syntax for assigning the result back to the property.

Example :

I have a list of terms such as the one below and would like to normalize their 'Frequency'.

public class Term
{
    public string Name { get; set; }
    public double Frequency { get; set; }
    public double Weight { get; set; }
}

Using some syntax like this I should be able to specify the property I am doing the operation on :

List<Term> list = Normalize(artist.Terms, s => s.Frequency);

(here it's 'Frequency' on 'Term' but I should be able to do that on any property of any type, property type will always be a double)

So that's what I crafted but I can't find out how to perform the operation nor assigning the result back to the property :

private static List<T> Normalize<T>(List<T> elements, Func<T, double> func)
{
    List<T> list = new List<T>();
    double fMin = elements.Min(func);
    double fMax = elements.Max(func);
    double fDelta = fMax - fMin;
    double fInv = 1.0d / fDelta;
    for (int i = 0; i < elements.Count; i++)
    {
        T t = elements[i];

        // What should I do from here ?
        //double invoke = func.Invoke(term);
        //term.Frequency = (term.Frequency - fMin) * fInv;
     }
    return list;
}

How would you achieve this ?

like image 920
aybe Avatar asked Jul 05 '26 22:07

aybe


1 Answers

If you want to avoid using expressions and reflection, you can simply provide both a getter function and a setter function. I also slightly updated the method since it would be altering the objects in original list, so I didn't think it'd be wise to create and return a new list; the API would be deceptive. (you can easily switch it back if desired)

private static void Normalize<T>(List<T> elements, Func<T, double> getter, Action<T, double> setter)
{
    double fMin = elements.Min(getter);
    double fMax = elements.Max(getter);
    double fDelta = fMax - fMin;
    double fInv = 1.0d / fDelta;
    for (int i = 0; i < elements.Count; i++)
    {
        T t = elements[i];

        double initialValue = getter(t);
        double newValue = (initialValue - fMin) * fInv;
        setter(t, newValue);
    }
}

With usage like:

Normalize(terms, t => t.Frequency, (t, normalizedFrequency) => t.Frequency = normalizedFrequency);

And of course it's easy to update to be an extension method so it could be used like:

terms.Normalize(t => t.Frequency, (t, normalizedFrequency) => t.Frequency = normalizedFrequency);

With this change in the Normalize signature, it's clear you are altering the existing list and not producing a new one. With the old signature, it would appear you are leaving the old list and objects untouched which is not the case.

like image 198
Chris Sinclair Avatar answered Jul 07 '26 13:07

Chris Sinclair