Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linq avoid calling function twice

Using Linq to Objects a query might need to filter based on the result of a function and return the value of that function.

For example

files.Where(x => string.IsNullOrWhiteSpace(x.getProperty(propName)))
                .GroupBy(x => x.getProperty(propName));

Does the compiler recognize that the value is going to be required for grouping and keep it?

If it doesn't then there must be a way to select to an anonymous type and query the Where and GroupBy statements against that. Is it possible to do this with an anonymous type?

I am able to declare a class and use that.

class fileSelector
{
    internal string prop;
    internal myFile file;
}

var groups = files
     .Select(x => new fileSelector() { prop = x.getProperty(propName),  file = x })
     .Where(x => !string.IsNullOrWhiteSpace(x.prop))
     .GroupBy(x => x.prop);

But is there a way to do this with an anonymous type?

This is what I tried for an anonymous type

var groups = files.Select(x => new { x.getProperty(propName),  x })
                  .Where(x => !string.IsNullOrWhiteSpace(x.prop))
                  .GroupBy(x => x.prop);

But this gives the error

Invalid anonymous type member declarator. Anonymous type members must be declared with a member assignment, simple name or member access.

Final answer

var groups = files
     .Select(x => new { prop = x.getProperty(propName),  file = x })
     .Where(x => !string.IsNullOrWhiteSpace(x.prop))
     .GroupBy(x => x.prop, x => x.file);
like image 519
Adam Avatar asked May 18 '26 02:05

Adam


1 Answers

Does the compiler recognize that the value is going to be required for grouping and keep it?

No, since getProperty might have an intended side effect.

If it doesn't then there must be a way to select to an anonymous type and query the Where and GroupBy statements against that. Is it possible to do this with an anonymous type?

Yes. Your code should work as-it-is by just replacing new fileSelector() {...} with new {...}. Note, though, that in your code (and in the modified version using the anonymous type), the elements of the grouping are fileSelector and the anonymous type, not myFile. See Scott Chamberlain's solution for how to fix that.

Alternatively, you could use the let clause to store intermediary values:

var groups = from file in files
             let prop = file.getProperty(propName)
             where !string.IsNullOrWhiteSpace(prop)
             group file by prop;
like image 106
Heinzi Avatar answered May 19 '26 17:05

Heinzi