4

I have always found it annoying when I need to write a condition that compares the same item over and over again since I would have the type the item so many times:

string x = textBox1.Text;

if (x == "apple" || x == "orange" || x == "banana" ...)
...

I want something like this (but of course this is not correct syntax):

if (x == "apple" || "orange" || "banana" ...)

Is there a solution other than using an array of the strings?

Kevin Cho
  • 491
  • 1
  • 6
  • 16

10 Answers10

10

Your condition says: I'm true if I match any of the predefined values. In other words if I'm an element of a predefined set which is semantically the Contains method:

if (new [] { "apple", "orange", "banana" }.Contains(x))
{

}

Using an array provides much more flexibility in the future. You can extract it out, reuse it, store it, chache it etc. I always use "arrays and loops" when I have to deal with more than 2 known values.

Note: As Scott Chamberlain pointed out in the comments with using HashSet<T>.Contains greatly improves the performace:

var values = new HashSet<string> { "apple", "banana", "orange" };
if (values.Contains(x))
{

}
nemesv
  • 138,284
  • 16
  • 416
  • 359
  • 3
    Under certain circumstances (but not always) it will be worth saving the array as a field and re-using it rather than creating it just for this one use. – Servy Jul 24 '12 at 19:37
  • Will the compiler optimize this away to be just as efficient as the switch statements? I mean you are instantiating things, (which is slow compared to other operations) – James Jul 24 '12 at 19:41
  • I don't think it's match better to support. Same values duplication everywhere. – Sergey Berezovskiy Jul 24 '12 at 19:47
  • @lazyberezovsky I think you misunderstood the question. The duplication they want eliminated is explicitly stating the variable name, not the items that are being compared to – James Jul 24 '12 at 19:53
  • @James sorry then.. He provided same values in both examples, thus I decided he is warring on that kind of duplication. Anyway, complex conditions should be simplified :) – Sergey Berezovskiy Jul 24 '12 at 19:56
  • 1
    @James Efficiency doesn't really matter much. Since the array will be small in size (or else neither approach is appropriate in the first place) it won't take long to run regardless of how well it's optimized. The purpose is to determine a method that can be efficiently written and easily understood. – Servy Jul 24 '12 at 19:57
  • 1
    You should store this has a hashset, not a array, even for a few items if it is being called very frequently it can cause a noticeable performance difference (I ran that exact code you posted when I was filtering out illegal characters in a stream being fed in to a XmlStreamReader, switching from array to HashSet greatly boosted performance) – Scott Chamberlain Jul 24 '12 at 20:36
  • 2
    @ScottChamberlain For ~3 items it shouldn't matter much at all. if you're going to have 5+ then yes, a HashSet would be better. If you're noticing a *significant* difference then my guess is you're storing the hashset outside of the loop and re-creating the array inside the loop (see my first comment here) which would account for a significant difference. Also note a `HashSet` is pointless unless outside the loop and re-used. If you define it inline then creating the `HashSet` costs more than you save. – Servy Jul 24 '12 at 20:59
  • @Servy I did have approx 7 items in my list. so I concede the point. – Scott Chamberlain Jul 24 '12 at 21:20
7

What about an extension method?

public static class Extensions
{
   public static bool IsOneOf<T>(this T input, params T[] possibilites)
   {
      bool result = possibilites.Contains(input);
      return result;
   }
}

You could then rewrite your code to look like this:

string input = textBox1.Text;
if(input.IsOneOf("apple", "orange", "banana"))
{
    // ....
}
Nikola Anusev
  • 6,940
  • 1
  • 30
  • 46
  • It should be `string input = textBox1.Text;`. Otherwise a nice extension. +1 – Avada Kedavra Jul 24 '12 at 19:41
  • 1
    That does not describe *why* you verifying if input is one of strings. Also you should duplicate all these parameters every time. – Sergey Berezovskiy Jul 24 '12 at 19:43
  • @AvadaKedavra Thanks for the catch, fixed. – Nikola Anusev Jul 24 '12 at 19:44
  • @lazyberezovsky Indeed, it does not. But I don't think that's the problem since the method is general - the context in which it is used will tell you *why*. Think LINQ's `Where` method, I think it's same here. – Nikola Anusev Jul 24 '12 at 19:47
  • You name your local variables `x`, `y`, `z` because its already clear from context what they mean? I think you always should provide additional information for other developers, and simplify context as much as you can. – Sergey Berezovskiy Jul 24 '12 at 19:50
  • @lazyberezovsky No, I don't. But how are you using LINQ's `Where` method? If you really need to simply filter out unwanted items from the collection, do you wrap the `Where` in *another* private method? I think that `Where` method reads simply enough for everyone to understand what's going on. Of course, if instead of `Where` there is a complicated chain of multiple methods, it's certainly better to wrap it all in one private method to give more context to the operation. And it's exactly the same with my `IsOneOf` method. – Nikola Anusev Jul 24 '12 at 19:57
  • @NikolaAnusev actually I use method `GetExpiredOrders` instead of `Where(order => order.IsExpired)`. It does not pulls you out of context to examine where conditions. – Sergey Berezovskiy Jul 24 '12 at 20:08
  • 1
    @lazyberezovsky Now I understand your reasoning, although I consider such a wrapping to be too extreme. I think that single `Where` is simple and descriptive enough to stand on its own and the same applies to my `IsOneOf` extension. – Nikola Anusev Jul 24 '12 at 20:20
1

You can move duplicated code to the method, which also will explain why this code is duplicated - because it verifies whether something is fruit. It will increase readability and maintainability of your code. Also you will be able to refactor this logic (e.g. turning it into switch statement):

private bool IsFruit(string name)
{
   switch(name)
   {
       case "apple":
       case "orange":
       ...
       case "banana":
           return true;
       default:
           return false;
   }
}

Usage:

string x = textBox1.Text;
if(IsFruit(x))
   ...

UPDATE: Better do not use such complex conditions - it's really hard to understand sometimes. You can use Introduce Explaining Variable or Extract Method (as above) refactorings to make your code more clear.

Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
1

Your best bet (in terms of performance) is to use HashSet

    static HashSet<string> Fruits = new HashSet<string> {"apple", "banana", "orange"};

    string x = textBox1.Text;
    if( Fruits.Contains( x)) {

Once you get beyond three or so possibilities in the if condition, a HashSet will run faster than straight comparison.

user980717
  • 251
  • 1
  • 9
0

linq solution

var strarray = new string[]
        {
            "apple",
            "orange",
            "banana"
        };

bool a = strarray.Any( x=> x ==  textBox1.Text);
if(a)
 //CODE 
else 
 //code

try switch case like this

Switch(value)
{
  case "apple":
  case "orange":
  case "banana":....
    //code you want
  break;
}
Pranay Rana
  • 175,020
  • 35
  • 237
  • 263
0

use a switch statement with like so

switch (x)
{
   case "apple":
   case "orange":
   case "banana":
      //code
      break;
}
James
  • 2,445
  • 2
  • 25
  • 35
0

You could try a switch statement:

switch (x)
{
    case "apple":
    case "orange":
    case "banana":
        //...
        break;
}
phoog
  • 42,068
  • 6
  • 79
  • 117
0

You can use a switch statement:

switch(x)
{
    case "apple":
    case "orange":
    case "banana":
        // "if" code goes here
        break;
    default:
        // "else" code goes here
        break;
}
Kris Vandermotten
  • 10,111
  • 38
  • 49
0

I like the extension method solution and have used it before. Here are the methods I have available in my "CommonUtils" library:

    public static bool IsIn<T>(this T toFind, IEnumerable<T> collection)
    {
        return collection.Contains(toFind);
    }

    public static bool IsIn<T>(this T toFind, ICollection<T> collection)
    {
        return collection.Contains(toFind);
    }

    public static bool IsIn<T>(this T toFind, params T[] items)
    {
        return toFind.IsIn(items.AsEnumerable());
    }

Between these three, you can pretty much use any collection, and you can also specify the items as a parameter list.

KeithS
  • 70,210
  • 21
  • 112
  • 164
-2

You could do something like this:

string it = "apple,orange,banana";

if(it.Contains(x))
{
  //do work
}

Even simpler:

if("apple,orange,banana".Contains(x))
    {
      //do work
    }
MikeTWebb
  • 9,149
  • 25
  • 93
  • 132
  • 3
    This is dangerous, inputs for x like "ban" would be true. You're better off donig `"apple,orange,banana".Split(',').Contains(x)` – James Jul 24 '12 at 19:37
  • Please explaing the Down Vote – MikeTWebb Jul 24 '12 at 19:37
  • 1
    @James Even better than splitting is to just define the structure by adding items rather than a delimited string. [example](http://stackoverflow.com/a/11638023/1159478) – Servy Jul 24 '12 at 19:38