2

I am working on some reporting code and I have found myself repeating a certain piece of code which contains a where and select cause over and over again? The only piece of information changing is the target field.

    private static void UpdateResultsListInstructions(List<ManagementInfo> managementInfo, KeyValuePair<int, int> item)
    {
        managementInfo
            .Where(m => m.YearMonthNo == item.Key)
            .Select(m => m.VolumeOfInstructionsReceivedInMonth = item.Value)
            .ToList();
    }

    private static void UpdateResultsListClaims(List<ManagementInfo> managementInfo, KeyValuePair<int, int> item)
    {
        managementInfo
            .Where(m => m.YearMonthNo == item.Key)
            .Select(m => m.VolumeOfClaimsCancelled = item.Value)
            .ToList();
    }

Is it possible to pass in the selector as an argument so I can reuse one method?

Along the lines of

UpdateCommonResultsList(managementInfo, item, (m => m.VolumeOfClaimsCancelled = item.Value))

I can probably rejig all the code so that this is not necessary but now that I've had the thought I'd like to see if its possible. Any help appreciated.

fluent
  • 2,393
  • 4
  • 22
  • 32
  • Your current code is assigning values in the select clasuse. Is it your intention to cause this effect (it could be why you're doing a ToList and not returning anything). – René Wolferink Nov 12 '12 at 17:18
  • Is there a particular reason you're using Linq extension methods to do what it seems a foreach loop would be better at? – cHao Nov 12 '12 at 18:06
  • No particular reason other than i just read the chapter on Linq in the Jon Skeet book and thought I should give it a test drive :) When you say a foreach would be better in what respect? I thought that these linq extensions resolved to pretty much the same il code as a foreach. – fluent Nov 12 '12 at 20:46
  • Not quite. The Linq stuff is a bunch of methods, whereas `foreach` is a language construct, so they'll never compile to the exact same thing. If you use a method that forces iteration, like `ToList()`, then yes, it'll end up doing a foreach internally. But frankly, why bother? You could say `.Select(...)` to modify your objects, but as noted in one of the comments above, it's unconventional and people will assume it's an accident. You could say `.ToList().ForEach(...)`, but you've gained nothing outside of a potentially massive List you won't even return. – cHao Nov 12 '12 at 22:17

4 Answers4

4

Although I don't like the use of Select to update a list - I misread the question because it's not usual to do that! - you can do it as follows:

private static void UpdateCommonResultsList(
    List<ManagementInfo> managementInfo,
    KeyValuePair<int, int> item,
    Action<ManagementInfo, int> action)
{
    managementInfo
        .Where(m => m.YearMonthNo == item.Key)
        .Select(m => { action(m, item.Value); return item.Value; })
        .ToList();
}

Call as

UpdateCommonResultsList(
    managementInfo,
    item,
    (m, i) => m.VolumeOfClaimsCancelled = i);

Your code will be a lot more conventional if you do this as

private static void UpdateCommonResultsList(
    List<ManagementInfo> managementInfo,
    KeyValuePair<int, int> item,
    Action<ManagementInfo, int> action)
{
    foreach (ManagementInfo m in managementInfo
        .Where(m => m.YearMonthNo == item.Key))
    {
        action(m, item.Value);
    }
}
Rawling
  • 49,248
  • 7
  • 89
  • 127
  • Don't use `Action`, but `Func` as the Select requires a result. But ideally, in your last bit of code, do `action(m)` with the action being `Action` and then it doesn't need anything else. – René Wolferink Nov 12 '12 at 17:26
  • 2
    I've been sat on the Tube for 20 minutes going "I hope nobody notices that didn't compile before I can fix it"... Fat chance :p – Rawling Nov 12 '12 at 17:56
  • Awesome, many thanks for answering my question and pointing me in a better direction than using select for an update. Thanks again. – fluent Nov 12 '12 at 21:40
2

The Select method takes an argument of type Func<TSource, TResult> so you can use that type in the method, and then pass it in as a lambda expression.

ChrisPatrick
  • 984
  • 7
  • 19
1

You can pass action like this to update methods:

UpdateResultsListClaims(managementInfo,
  (m) => m == someItem.Key,
  (m, newData) => m.VolumeOfClaimsCancelled = someItem.Value)

private static void UpdateResultsListClaims(List<ManagementInfo> managementInfo,
Func<bool> updateCondition, Action<ManagementInfo> updateAction)
    {
        managementInfo
            .Where(m => updateCondition(m))
            .ToList()
            .ForEach(m => updateAction(m));
    }

UPDATE 1 For more readability you can call:

UpdateResultsListClaims(managementInfo,
  updateCondition: (m) => m == someItem.Key,
  updateAction: (m, newData) => m.VolumeOfClaimsCancelled = someItem.Value)

UPDATE 2 For easy use in many places you can make extension method like this

public static void UpdateResults(this List<ManagementInfo> managementInfo,
Func<bool> updateCondition, Action<ManagementInfo> updateAction)
    {
        managementInfo
            .Where(m => updateCondition(m))
            .ToList()
            .ForEach(m => updateAction(m));
    }

and call it like this

managementInfo.UpdateResults(
  updateCondition: (m) => m == someItem.Key,
  updateAction: (m, newData) => m.VolumeOfClaimsCancelled = someItem.Value)
gabba
  • 2,815
  • 2
  • 27
  • 48
  • 1
    Any particular reason not to say something like `foreach (var m in managementInfo.Where(updateCondition)) { updateAction(m); }` rather than creating a List? – cHao Nov 12 '12 at 18:01
  • cHao, the reason is just little less typing 8) By the way did some one know why in linq ForEach did't exists for IEnumerable? – gabba Nov 12 '12 at 19:03
  • here's discusson about foreac for ienumerable http://stackoverflow.com/questions/200574/linq-equivalent-of-foreach-for-ienumerablet – gabba Nov 12 '12 at 19:08
  • Because the biggest feature of IEnumerable, besides the extension methods, is right in the name -- it is designed to be *enumerable*; that is, it can be used in a foreach loop. A ForEach method wouldn't provide any functionality not already provided by C# and VB. – cHao Nov 12 '12 at 19:08
  • In case above, it can improve the readability and reduce typing. Even with codecomplete and snippets. But I vote up your comment both hands - list creation not optimal – gabba Nov 12 '12 at 19:19
0
private static void UpdateCommonResultsList<T>(List<ManagementInfo> managementInfo, KeyValuePair<int, int> item, Func<ManagementInfo,T> updateFunc)
    {
        managementInfo
            .Where(m => m.YearMonthNo == item.Key)
            .Select(m => updateFunc(m))
            .ToList();
    }

And then invoke as:

UpdateCommonResultsList(managementInfo, item, (m => m.VolumeOfClaimsCancelled = item.Value));
René Wolferink
  • 3,558
  • 2
  • 29
  • 43