72

Is there a shorter way of writing something like this:

if(x==1 || x==2 || x==3) // do something

What I'm looking for is something like this:

if(x.in((1,2,3)) // do something
Cristian Ciupitu
  • 20,270
  • 7
  • 50
  • 76
user1854438
  • 1,784
  • 6
  • 24
  • 30
  • 6
    `if(x>=1 && x<=3)`? – vcsjones May 31 '13 at 21:22
  • 3
    What's wrong with your way? Do you have *many more* comparisons? Because the former (for only 3 or even 4 comparisons) is usually more readable than the latter. – dlev May 31 '13 at 21:23
  • 2
    What's wrong with the first one? It's short enough, explains itself and is as efficient as you're going to get. – Dave Zych May 31 '13 at 21:23
  • 3
    +1 When you do `x == A || x == B || x == C || ... || x == Z` what you are expressing is: `x is in the set {A, B, C, ..., Z}?` hence the most clear syntax would be `x in {A,B,C, ..., Z}`. The `||` should be used when the conditions are different one from the other. – Bakuriu Jun 01 '13 at 07:38

8 Answers8

75

You could achieve this by using the List.Contains method:

if(new []{1, 2, 3}.Contains(x))
{
    //x is either 1 or 2 or 3
}
It'sNotALie.
  • 22,289
  • 12
  • 68
  • 103
Ilya Ivanov
  • 23,148
  • 4
  • 64
  • 90
  • I put this in the same class – user1854438 May 31 '13 at 22:01
  • When I run this, I get "System.Array does not implement Contains". You are attempting to apply a list method, which exists within System.Collections.Generic against an array method, which exists within System. – J Weezy Dec 10 '20 at 21:30
  • In .Net Core 5.x this method does not seem to work. Had todo it like this `int x = 1; if((new List {1, 2, 3}).Contains(x)) {}` – Large Oct 24 '21 at 22:42
  • 1
    @JWeezy "Contains" is an extension method for implementations of IEnumerable (e.g. System.Array). However, you'll have to bring the "System.Linq" namespace into scope before you can use it. – JustALawnGnome7 Nov 29 '22 at 23:01
  • 1
    The only thing I might do differently here would be to use "stackalloc" instead of "new": this will allocate memory on the stack instead of the heap, leaving one less thing for GC to clean up. – JustALawnGnome7 Nov 29 '22 at 23:03
41
public static bool In<T>(this T x, params T[] set)
{
    return set.Contains(x);
}

...

if (x.In(1, 2, 3)) 
{ ... }

Required reading: MSDN Extension methods

Austin Salonen
  • 49,173
  • 15
  • 109
  • 139
  • 5
    Can even update this to use `params` so you'd get something like `x.In(1, 2, 3)` EDIT: Of course, at that point you _could_ write `x.In()` which is a bit silly. (on the other hand, avoids the existing `IEnumerable` version where you could pass in `x.In(null)` and throw an exception) – Chris Sinclair May 31 '13 at 21:24
  • 3
    Could be better, have you heard of `params`? – It'sNotALie. May 31 '13 at 21:24
  • 2
    While this is definitely *elegant*, I personally *hate* extensions methods that apply to everything. +1 just the same, though: I like elegance :) – dlev May 31 '13 at 21:25
  • I keep getting this error "extension method can only be declared in non-generic non-static class' – user1854438 May 31 '13 at 22:01
  • @user1854438: No. this has to be in a separate public class. I've updated the answer to include a link that should get you going on extension methods. – Austin Salonen May 31 '13 at 22:02
  • @dlev: I generally agree with you there. This isn't something I would really use as it's not my style but it does what OP wants. – Austin Salonen May 31 '13 at 22:03
14

If it's in an IEnumerable<T>, use this:

if (enumerable.Any(n => n == value)) //whatever

Else, here's a short extension method:

public static bool In<T>(this T value, params T[] input)
{
    return input.Any(n => object.Equals(n, value));
} 

Put it in a static class, and you can use it like this:

if (x.In(1,2,3)) //whatever
Robert McKee
  • 21,305
  • 1
  • 43
  • 57
It'sNotALie.
  • 22,289
  • 12
  • 68
  • 103
  • And FGITW strikes again! **Ugh.** – It'sNotALie. May 31 '13 at 21:25
  • I think you want `n => object.Equals(n, value)`, since a) that guards against `null`, and b) ensures that non-value types have a chance at value equality. (I think using `Contains()` instead of `Any()` will accomplish the same thing.) – dlev May 31 '13 at 21:27
  • Oh and dont forget the closing parenthesis – Isaac May 31 '13 at 21:29
2
int x = 1;
if((new List<int> {1, 2, 3}).Contains(x))
{
}
RacerNerd
  • 1,579
  • 1
  • 13
  • 31
2

Starting with C#9 the language supports a pattern for this:

if(x==1 || x==2 || x==3) foo();

can be written as:

if (x is (1 or 2 or 3)) foo();

Those patterns can be pretty complex. I found a description for this case here: https://learn.microsoft.com/en-US/dotnet/csharp/language-reference/operators/patterns#precedence-and-order-of-checking

J. Gofy
  • 21
  • 3
0

I'm entirely guessing here, correct the code if I'm wrong:

(new int[]{1,2,3}).IndexOf(x)>-1
Isaac
  • 11,409
  • 5
  • 33
  • 45
-4

You can create a simple Dictionary<TKey, TValue> that'll be used as a Decision Table for that problem:

        //Create your decision-table Dictionary
        Action actionToPerform1 = () => Console.WriteLine("The number is okay");
        Action actionToPerform2 = () => Console.WriteLine("The number is not okay");
        var decisionTable = new Dictionary<int, Action>
            {
                {1, actionToPerform1},
                {2, actionToPerform1},
                {3, actionToPerform1},
                {4, actionToPerform2},
                {5, actionToPerform2},
                {6, actionToPerform2}
            };

        //According to the given number, the right *Action* will be called.
        int theNumberToTest = 3;
        decisionTable[theNumberToTest](); //actionToPerform1 will be called in that case.

Once you've initialized your Dictionary, all left to do is:

decisionTable[theNumberToTest]();

Yair Nevet
  • 12,725
  • 14
  • 66
  • 108
  • 4
    This is one of the smallest individual units of over-engineering I've ever seen. Given the context. – Grant Thomas May 31 '13 at 22:14
  • This is one of the cleanest and fastest methods. This is all what you need to do to perform your `if` condition: `decisionTable[theNumberToTest]();` – Yair Nevet May 31 '13 at 22:19
  • 1
    I like it when people use O(?) to try and prove something is 'faster' than another. It smells of fresh meat with a hint of mint. – NPSF3000 May 31 '13 at 22:23
  • If you're only doing the test once, then this could easily be slower than `Contains` -- building the dictionary is O(n), just like building & scanning the list. – Ryan M May 31 '13 at 22:24
  • @Ryan The `Dictionary` can be build only once. – Yair Nevet May 31 '13 at 22:25
  • Right, but if the test is only being done once, you don't gain anything. If the test is performed any constant number of times, both approaches are still O(n). When the number of tests is small, the cost to build the dictionary could easily dominate. Of course, you'd have to profile some actual code to see which approach is faster in real life, which is what I believe NPSF3000 is getting at. Not to mention, it's very unlikely that this test will be a performance bottleneck, so the performance question is moot. – Ryan M May 31 '13 at 22:37
  • @Yair O(?) only tells you how something scales, not how performant it is. – NPSF3000 Jun 01 '13 at 02:41
-4

This answer refers to a possible future version of C# ;-) If you consider switching to Visual Basic, or if Microsoft finally decides to introduce the Select Case statement to C#, it would look like this:

Select Case X
    Case 1, 2, 3
    ...
End Select
Tigerfink
  • 69
  • 1
  • 10