VVS
VVS

Reputation: 19612

Get first item that meets a criteria or return first item

I want to get the first item that meets a given criteria and if none of the items match that criteria, I just want to get the first item

This is what I currently do and what works, but I don't like it. Is there a more elegant way of doing the same in just one LINQ query?

LookupLine lookup;
Func<KeyValuePair<int,LookupLine>,bool> criteria = 
    (kv) => !kv.Value.Gruppe.StartsWith("SF");

if (lookupLines.Any(criteria))
{
    lookup = lookupLines.First(criteria).Value;
}
else if (lookupLines.Any())
{
    lookup = lookupLines.First().Value;
}

FirstRelevantGroup = lookup.Gruppe;

Upvotes: 1

Views: 4826

Answers (3)

John Melville
John Melville

Reputation: 3835

My favorite one liner for this is

source.Where(predicate).Concat(source).FirstOrDefault()

This returns

  • The first matching element if there is one.
  • The first element if note match.
  • Null if the list is empty.

Upvotes: 1

Cheng Chen
Cheng Chen

Reputation: 43523

I'd like an extension method.

public static T FirstIfNotFound<T>(this IEnumerable<T> source, Func<T,bool> predicate)
{
   if (source==null||predicate==null) throw new ArgumentNullException();
   return source.Any(predicate) ? source.FirstOrDefault(predicate) : source.First();
}

Upvotes: 1

David Hedlund
David Hedlund

Reputation: 129802

Well, for a one-liner, you could do

lookupLines.OrderBy(kv => kv.Value.Gruppe.StartsWith("SF") ? 2 : 1).First();

But I think your solution better conveys your intentions, although it could be simplified to:

LookupLine lookup = lookupLines.FirstOrDefault(kv => !kv.Value.Gruppe.StartsWith("SF"));
if(lookup == null) {
   lookup = lookupLines.First();
}

FirstRelevantGroup = lookup.Value.Gruppe;

Now, you could turn the above into a coalesce, but again you'd be losing some readability:

FirstRelevantGroup = (
     lookupLines.FirstOrDefault(kv => !kv.Value.Gruppe.StartsWith("SF"))
     ?? lookupLines.First()
).Value.Gruppe;

Upvotes: 5

Related Questions