Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test if value equals any of the values #1787

Closed
opcodewriter opened this issue Aug 14, 2018 · 7 comments
Closed

Test if value equals any of the values #1787

opcodewriter opened this issue Aug 14, 2018 · 7 comments

Comments

@opcodewriter
Copy link

opcodewriter commented Aug 14, 2018

Have a way to be able to write the following statement in a simpler, concise way:
if (value == 1 || value == 2 || value == 3)

A solution maybe is to just add to System.ValueTuple a method like Contains(object o) which would allow us to write:
if ((1, 2, 3).Contains(value))

I am aware I could use Linq and just write (new int[] {1, 2, 3}).Contains(value) but this involves allocation.

@AustinBryan
Copy link

public static bool Contains(this (int a, int b, int c) t, int value) => value == t.a || value == t.b || value == t.c;   

Yes this can be a bit of a pain, as you'd need 5 or 6 methods just for int, but there it is.
However, if you make it generic:

public static bool Is<T>(this T value, (T a, T b, T c) t) => value.Equals(t.a) || value.Equals(t.b) || value.Equals(t.c);   
...
Console.WriteLine(4.Is((1, 2, 3)));

(A better name might be IsAny). That solves that problem. Also, it makes more sense to do it value.Is((item1, item2...)) anyways. The only problem that remains is that you have to create a method for each number of items (2 ints, 3 ints, etc.) That's fine because C# already gives us a solution for that.

public static bool Is<T>(this T value, params T[] ts)
{
    foreach (var t in ts)
        if (t.Equals(value)) return true;
    return false;
}
...
4.Is(1, 2, 3);

This has three benefits:

  1. It removes the double parans (4.Is((1, 2, 3)) vs 4.Is(1, 2, 3) which asthetically is nicer
  2. This one method allows for 4.Is(1) and all the way up to and beyond 4.Is(1, 2, 3, 4, 5, 6, 7, 8...)
  3. Works for all types because it's generic

Downside, on very large scales it's slower than tuples by around 10x.

The test that I ran:

public static class Ext
{
    public static bool Is<T>(this T value, (T a, T b, T c) t) => value.Equals(t.a) || value.Equals(t.b) || value.Equals(t.c);
    public static bool Is<T>(this T value, params T[] ts)
    {
        foreach (var t in ts)
            if (t.Equals(value)) return true;
        return false;
    }
}

...

var stopwatch = new Stopwatch();

// -- Tuple Test --
stopwatch.Start();

for (int i = 0; i < 10_000_000; i++)
    4.Is((1, 2, 3));
stopwatch.Stop();
stopwatch.Elapsed.Log("Tuple");

// -- Params Test --
stopwatch.Reset();
stopwatch.Start();

for (int i = 0; i < 10_000_000; i++)
    4.Is(1, 2, 3);

stopwatch.Stop();
stopwatch.Elapsed.Log("Params");

Results (ran a few time, results more or less the same):

10,000,000 reps

Tuple: 00:00:00.4190570
Params: 00:00:03.1696387

10,000 reps

Tuple: 00:00:00.0012369
Params: 00:00:00.0045088

1000 reps

Tuple: 00:00:00.0005783
Params: 00:00:00.0006017

100 reps

Tuple: 00:00:00.0006235
Params: 00:00:00.0002104

As you can see, params is more or less the same speed, if not, faster, on the smaller, more realistic scales. Also, even when the scales are bigger, it's only slower by a fraction of a second more (except for the 10,000,000 reps test).

So you can use that, and the language doesn't need to be changed at all 😊

@opcodewriter
Copy link
Author

@AustinBryan Thanks a lot! Very cool ideas.

I prefer to have something built in either in the language or in the framework.
I know we can always write something ourselves, but wouldn't you agree since this is so very common, it's worth having it built-in?

@DavidArno
Copy link

Please see #185, #198, #316, #420, and likely others, for similar ideas.

@opcodewriter
Copy link
Author

@DavidArno wow, thanks! I think I will just close this one, doesn't make sense to have so many issues open on same subject. Thanks again. I hope this gets added in the language or framework.

@AustinBryan
Copy link

AustinBryan commented Aug 14, 2018

@opcodewriter Meh, you only have to write the extension method one time and worst case you copy paste or write right it (it's short anyways) in each project (which starting a new project shouldn't be all that common) but I'm sure there's a way to include the extension method file in all projects without having to manually do it. I just haven't found it out yet because it's not that big of a burden to copy and paste back and forth 😜

Also, if it is part of the language, I'd expect a syntax that isn't something that can be duplicated with extension methods, which is what all the other ones have suggested.

@theunrepentantgeek
Copy link

I'm sure there's a way to include the extension method file in all projects without having to manually do it.

Here's what I've done in the past: Wrap common code into a NuGet package that's consumed by every project that needs it - private publish to a folder/file-share, to MyGet, or to publically via NuGet.

Takes a sprinkle of automation to set up, but saves on copy-pasta repetition, and it becomes really easy to update.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants
@DavidArno @theunrepentantgeek @opcodewriter @AustinBryan and others