Extension method, SumIf on generic List<T>

社会主义新天地 提交于 2019-12-22 11:37:35

问题


I need to write an generic extension method for List(T) that conditionally considers each string property of T, then sums a corresponding decimal property of T if a condition is met. My effort thus far:

// foreach(p in Persons) { if(p.Name == "mort"){sum p.Amount;} }

public static double SumIf<T>(this T o, List<T> ListItems, 
          string targetStr, ?strVals?, ?dblVals?)
{
    double sum = 0;
    foreach(T item in ListItems)
    {
        if(item.?strVal? == targetStr){ sum += item.?dblVal? ; }
    }
    return sum;
}

Thanks for any guidance, mort


回答1:


It sounds like you want a way of extracting the string property and double property (assuming that the "decimal" in your post was a typo rather than the "double" in your code) - Func is appropriate here:

public static double SumIf<T>(this IEnumerable<T> source, 
          string targetText,
          Func<T, string> textSelector,
          Func<T, double> valueSelector)
{
    double sum = 0;
    foreach (T item in source)
    {
        if (textSelector(item) == targetText)
        {
            sum += valueSelector(item);
        }
    }
    return sum;
}

(Note that I've removed the unused initial parameter, and made it an extension method on the list itself. Not using the value feels like a bit of a smell to me... I've also changed the parameter type to IEnumerable<T> as you don't need it to be a list really.)

Note that this actually mostly equivalent to:

public static double SumIf<T>(this IEnumerable<T> source, 
          string targetText,
          Func<T, string> textSelector,
          Func<T, double> valueSelector)
{
    return source.Where(x => textSelector(x) == targetText)
                 .Sum(valueSelector);
}

I'd personally probably go for a general predicate function instead of a string and a text selector:

public static double SumIf<T>(this IEnumerable<T> source, 
          Func<T, bool> predicate,
          Func<T, double> valueSelector)
{
    return source.Where(predicate)
                 .Sum(valueSelector);
}

Then you'd call it with

double sum = list.SumIf(x => x.Name == "mort", x => x.Amount);

... which seems just as good to me as:

double sum = list.SumIf("mort", x => x.Name, x => x.Amount);

... but is considerably more flexible.

As noted in comments, do you really need this at all? Are you using it in sufficient places to make the simple Where/Sum calls unbearable? Heck, you can turn it into just a Sum call using the conditional operator:

double sum = list.Sum(x => x.Name == "mort" ? x => x.Amount : 0d);



回答2:


You introduce some very specific constraints to your method that make it that it cannot be generic, e.g. T must have a property Amount. It's better to pass those dependencies in as functions:

public static double SumIf<T>(this IList<T> source, 
                              Func<T, bool> pred, 
                              Func<T, double> val) 
{
    double sum = 0;
    foreach (var item in source)
        if (pred(item))
            sum += val(item);

    return sum;
}

Then you can just pass in your predicate and sum property selector as lambdas:

List<Person> people = new List<Person>();
people.Add(new Person() { Name = "Joe", Amount =20.2});
people.Add(new Person() { Name = "Fred", Amount = 11 });
people.Add(new Person() { Name = "Joe", Amount = 5.7 });

double sum = people.SumIf(x => x.Name == "Joe", x => x.Amount);


来源:https://stackoverflow.com/questions/9754502/extension-method-sumif-on-generic-listt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!