Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linq Select Where

I often find myself writing something like this:

var fields = _type.GetProperties()
            .Select(prop => new { Prop = prop, Attrib = prop.GetCustomAttribute<ColumnAttribute>() })
            .Where(t => t.Attrib != null)
            .ToList();

Where I'm bothered is that I'm unnecessarily creating objects in the cases where the where clause fails. Granted the overhead is small, but I'd still prefer to save the allocation, as I would if I were simply looping over it or did the more painful:

var fields = _type.GetProperties()
        .Select(prop =>
        {
            var attrib = prop.GetCustomAttribute<ColumnAttribute>();

            return attrib == null ? null : new {Prop = prop, Attrib = attrib};
        })
        .Where(t => t != null);

Is there a better pattern/extension method I'm missing? Or is it possible that LINQ could make that optimization under the covers?

Much appreciated!

Update:

I guess something like this is what I mean, but I'm expecting something equivalent already exists and I'm just searching poorly:

public static IEnumerable<TResult> SelectWhereNotNull<TSource, TValue, TResult>(this IEnumerable<TSource> source, Func<TSource, TValue> valueSelector, Func<TSource, TValue, TResult> selector)
    where TValue:class
    where TResult:class
{
    return source
        .Select(s =>
        {
            var val = valueSelector(s);

            if (val == null)
            {
                return null;
            }

            return selector(s, val);
        })
        .Where(r => r != null);
}

var fields = _type.GetProperties()
     .SelectWhereNotNull(prop => prop.GetCustomAttribute<ColumnAttribute>(), Tuple.Create);
like image 781
Gene Avatar asked May 31 '14 22:05

Gene


People also ask

What is difference between select and where in LINQ?

Select will always return the same number of elements in the list (regardless of a filter condition you may have). Where can return less elements depending on your filter condition.

How do I select a query in LINQ?

Select query in LINQ Select method is used to select one or more items from collection or list object, here we see some example of linq select statement. variableName. Select(s => s.Name); There are various ways we can select some records or single record from a collection object.

How does select work in LINQ?

The Select() method invokes the provided selector delegate on each element of the source IEnumerable<T> sequence, and returns a new result IEnumerable<U> sequence containing the output of each invocation.

How do you select in C#?

In a query expression, the select clause specifies the type of values that will be produced when the query is executed. The result is based on the evaluation of all the previous clauses and on any expressions in the select clause itself. A query expression must terminate with either a select clause or a group clause.


1 Answers

For the type of query you're performing, you can't really get around it. You will want to have a place to put that attribute somewhere. Whether you hide it in a separate method or operate on your result object, it has to be done. It would be counter productive to worry about it. But there are ways you can make it more readable.

If you rewrote your query in the query syntax, you can hide the fact that it is being done

var fields =
    from prop in _type.GetProperties()
    let attr = prop.GetCustomAttribute<ColumnAttribute>()
    where attr != null
    select new
    {
        Prop = prop,
        Attrib = attr,
    };

For this however, I would probably package it up in a generator. It doesn't need to be written in terms of LINQ, you'd be seriously limiting yourself if you try to.

public static IEnumerable<TResult> SelectWhere<TSource, TValue, TResult>(
        this IEnumerable<TSource> source,
        Func<TSource, TValue> valueSelector,
        Func<TSource, TValue, bool> predicate,
        Func<TSource, TValue, TResult> resultSelector)
{
    foreach (var item in source)
    {
        var value = valueSelector(item);
        if (predicate(item, value))
            yield return resultSelector(item, value);
    }
}

Your query becomes this:

var fields = _type.GetProperties()
    .SelectWhere(
        p => p.GetCustomAttribute<ColumnAttribute>(),
        (p, a) => a != null,
        (p, a) => new { Prop = p, Attrib = a }
    )
    .ToList();
like image 53
Jeff Mercado Avatar answered Oct 17 '22 18:10

Jeff Mercado