tags:

views:

101

answers:

5

This is probably a basic LINQ question. I have need to select one object and if it is null select another. I'm using linq to objects in the following way, that I know can be done quicker, better, cleaner...

    public Attrib DetermineAttribution(Data data)
    {

        var one = from c in data.actions
                           where c.actionType == Action.ActionTypeOne
                           select new Attrib 
                                      {
                                          id = c.id,
                                          name = c.name
                                      };
        if( one.Count() > 0)
            return one.First();

        var two = from c in data.actions
                  where c.actionType == Action.ActionTypeTwo
                  select new Attrib
                             {
                                 id = c.id,
                                 name = c.name
                             };
        if (two.Count() > 0 )
            return two.First();

  }

The two linq operations differ only on the where clause and I know there is a way to combine them. Any thoughts would be appreciated.

A: 
 var one = (from c in data.actions
                           where (c.actionType == Action.ActionTypeOne) || (c.actionType == Action.ActionTypeTwo)
                           select new Attrib 
                                      {
                                          id = c.id,
                                          name = c.name
                                      }).FirstOrDefault();

This does not guarantee that ActionTypeOne will be found before ActionTypeTwo though. It finds the first record that is ActionTypeOne or ActionTypeTwo.

Raj Kaimal
You could use orderby to ensure the order in which items are retrieved.
codymanix
@codymanix: You'd have to specify a custom comparer so that elements of type `ActionTypeOne` are returned before elements of type `ActionTypeTwo` (there's no guarantee they will be sorted that way by default). It would also take something that can run in linear time and turn it into an n log(n). Doesn't matter 99% of the time, but something to be aware of.
R0MANARMY
+1  A: 

This doesn't use the query syntax, but it preserves the logic that an element of type ActionTypeOne is return before an element of ActionTypeTwo. And because of lazy evaluation, the second query won't be executed unless there are no elements of type ActionTypeOne.

public Attrib DetermineAttribution(Data data)
{

    return data.actions.Where( c => c.actionType == Action.ActionTypeOne)
              .Concat( data.actions.Where( c.actionType == Action.ActionTypeTwo ) )
              .Select( c => new Attrib
                         {
                             id = c.id,
                             name = c.name
                         })
              .FirstOrDefault();
}
R0MANARMY
A: 

I would suggest:

 public Attrib DetermineAttribution(Data data)
 {
        var types = Enum.GetValues(typeof (Action)).Cast<Action>();
        var merged = from c in data.actions
                     from t in types
                     where c.actionType == t
                     select new Attrib {id = c.id, name = c.name};

        return merged.FirstOrDefault();
 }
Erik Burigo
A: 
 var one =  from c in data.actions
                where c.actionType == Action.ActionTypeOne
                    || c.actionType == Action.ActionTypeTwo
                orderby c.actionType == Action.ActionTypeOne ? 0 : 1
                select new Attrib 
                                {
                                  id = c.id,
                                  name = c.name
                                }).FirstOrDefault();

The equivalent SQL query would be ( I used a Case statement because I did not know the data type of the ActionType column):

Select TOP 1 id, name
From Actions
where ActionType In(ActionTypeOne, ActionTypeTwo)
Order By Case ActionType When 1 Then 0 Else 1 End ASC
Thomas
+6  A: 

I think this solution is simple and efficient:

public Attrib DetermineAttribution(Data data)
{
    var c = data.actions.FirstOrDefault(c => c.actionType == Action.ActionTypeOne) ??
            data.actions.FirstOrDefault(c => c.actionType == Action.ActionTypeTwo);
    return c != null ? new Attrib { id = c.id, name = c.name } : null;
}
bruno conde
Definitely simpler
R0MANARMY