5
\$\begingroup\$

I wrote the following function to determine whether a string ends with one of a few given options.

I'm sure it can be written more elegantly, probably avoiding the loop.

bool EndsWithOneOf(string value, IEnumerable<string> suffixes)
{ 
    foreach(var suffix in suffixes)
    {
        if value.EndsWith(suffix)
            return true;
    }
    return false;
}
\$\endgroup\$
  • \$\begingroup\$ My previous answer was given multiple times, but this works too. Using a method group: bool EndsWithOneOf(string value, IEnumerable<string> suffixes) { return suffixes.Any(value.EndsWith); } \$\endgroup\$ – Abbas Aug 18 '11 at 17:48
  • \$\begingroup\$ @Abbas You can edit your current answer to include that if you like. No need to demote it to a comment! \$\endgroup\$ – dlev Aug 18 '11 at 17:50
  • \$\begingroup\$ Ok thanks, didn't know that! :) I put it in my previous answer. \$\endgroup\$ – Abbas Aug 18 '11 at 17:52
  • 1
    \$\begingroup\$ You can't enumerate without... you know, enumerating. Any implementation will involve a loop, and there's nothing wrong with that. That's what loops are for... no pun intended =) \$\endgroup\$ – Ed S. Aug 19 '11 at 0:06
  • \$\begingroup\$ Could also try some regex options depending on your use case \$\endgroup\$ – KyleMit Sep 28 '18 at 1:54
15
\$\begingroup\$

You can LINQify it to improve readability:

bool endsWithOneOf = suffixes.Any(x => value.EndsWith(x));

Note that this doesn't "avoid the loop", since Any() will iterate through the suffixes (stopping when it hits a match.) But that's ok, since how else could you do it? You have an enumeration of suffixes, so to do anything with them, you must enumerate them.

\$\endgroup\$
  • \$\begingroup\$ That's what I've been looking for, thanks a lot! \$\endgroup\$ – michael Aug 18 '11 at 17:49
  • \$\begingroup\$ No prob! In general, if you have a foreach loop, you should try to "Think LINQ". \$\endgroup\$ – dlev Aug 18 '11 at 17:50
  • \$\begingroup\$ @dlev - I guess that would depend on how legible the code looks after you LINQ-ify it. Sometimes you sacrifice readability in those instances, which makes maintaining (probably by other developers) a little more challengeing (well, it becomes an obstacle...just a small one). \$\endgroup\$ – Matthew Abbott Aug 18 '11 at 17:58
  • \$\begingroup\$ @Matthew That's a good point. Personally, I've found that LINQ very often makes my code more readable, but there are certainly cases where that's not true. \$\endgroup\$ – dlev Aug 18 '11 at 17:59
  • 2
    \$\begingroup\$ @Matthew: You should not have to don't dumb down your code just because a few people do not understand the syntax or the libraries. It's their responsibility to keep up it and using LINQ as a part of a .NET 3.5+ application has become the norm. This reminds me of this recent discussion on Software Engineering. \$\endgroup\$ – Jeff Mercado Aug 18 '11 at 22:29
11
\$\begingroup\$
bool EndsWithOneOf(string value, IEnumerable<string> suffixes)
{
    return suffixes.Any(suffix => value.EndsWith(suffix));
}

Edit: too late :D

Update

My previous answer was given multiple times, but this works too. Using a method group:

bool EndsWithOneOf(string value, IEnumerable<string> suffixes)
{
    return suffixes.Any(value.EndsWith);
}
\$\endgroup\$
  • \$\begingroup\$ +1 for reminding that you can use a method group. \$\endgroup\$ – Rhyous Aug 3 '17 at 16:17
4
\$\begingroup\$
static bool EndsWithOneOf(this string value, IEnumerable<string> suffixes)
{
    return suffixes.Any(suffix => value.EndsWith(suffix));
}

Two advantages here:

  1. use of extension method makes calling this method more straight forward (i.e. mystring.EndsWithOneOf(mySuffixes);)
  2. Use of linq allows for better readability (see declarative vs imperative programming)
\$\endgroup\$
3
\$\begingroup\$

Regardless of how you do it, at some point in the execution of a method like that, there will be a enumeration.

What you can do, is make the method more terse by using a LINQ method:

return suffixes.Any(s => value.EndsWith(s));

But is that any more readable than:

foreach (string suffix in suffixes) {
    if (value.EndsWith(suffix)) return true;
}
return false;

For actual method improvements, you should consider argument validation. What happens if value is null? Your current code will return in a NullReferenceException.

Behaviourly would it be correct to throw an ArgumentNullException in this case? Or is the possible null value an automatic resultant value of false?

You should also consider how your are comparing strings? Should you provide an overload that allows the caller to pass in an appropriate CultureInfo object? Or perhaps specify the StringComparison?

\$\endgroup\$
1
\$\begingroup\$

I dont think this is relevant given your question example, and therefore this answer may be pointless, but an alternative is that you can invert the comparison test to eliminate the enumeration.

For example, if you were looking for file type extensions, then instead of comparing the filename against a collection of extensions (like has been solved already), you can build a single string containing all the extensions (plus maybe an extra token), and then search for the input filename type in that string.

E.g. ".png.jpg.bmp.mov.mp3.".contains(myFileExtension + ".");

I've used this approach a few times to unravel a loop.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy