0

In c++, is there a way to see if a variable is equal to one of the values? Right now I have to do

if (fExt == "zip" | fExt == "7z" | fExt == "gz" | fExt == "tar")
    {
        //do something
    }

However, is there a more efficient way?

2 Answers2

5

Example with a set:

if (std::set<std::string>{"zip", "7z", "gz", "tar"}.count(fExt)) {
     std::cerr << "yes" << std::endl;
}

Note that std::set::count() returns 0 or 1 and provides effectively the same functionality as std::set::contains() which unfortunately is only introduced in C++20.

This is not too bad as it may skip a few comparisons. It will still not be more efficient, given the extra work to setup and teardown the set. But if your code is called more often, it gets better by re-using the set:

static const std::set<std::string> extensions{"zip", "7z", "gz", "tar"};
if (extensions.count(fExt)) {
    std::cerr << "yes" << std::endl;
}

Debatable whether it will be more efficient then doing the four string comparisons but it will probably also not be worse; and it might be easier to maintain.

You could also use an unordered_set, but for a low number of elements like in your case, it will do more unnecessary computation.

ypnos
  • 50,202
  • 14
  • 95
  • 141
  • Cant we use `find()` instead of `count()` because if the set contains a large number of elements then `find()` will return after we get the first occurrence whereas `count()` will check till the last element present in the set? – Ayush Jain Jul 27 '20 at 16:22
  • 1
    @AyushJain It doesn't matter, elements in a `set` are unique. – cigien Jul 27 '20 at 16:23
  • @cigien it is that possible misunderstanding that made me think twice if `count` is the best choice. Though this is complaining on a very high level ;) – 463035818_is_not_an_ai Jul 27 '20 at 16:24
  • 1
    @AyushJain The method `std::set::count()` can only return 0 or 1. The reason is that it does not count all elements in the set, but all elements that match the argument. And as a `set` holds unique elements, there can be at most one. Only on `std::multiset()` you may obtain a higher count. You could argue the method name should be `contains()` (which was added in C++20), but I assume `count()` was favored to be compatible to multiset. – ypnos Jul 27 '20 at 16:27
5

If you can use c++17, I would suggest a fold-expression:

template<typename T, typename ...Opts>
bool any_of(T val, Opts ...opts)
{
    return (... || (val == opts));
}

which you can then use like this:

if (any_of(fExt, "zip", "7z", "gz", "tar"))
{
  // ...
}

You should put this function into your own namespace to avoid any possible confusion with std::any_of from the <algorithm> header.

cigien
  • 57,834
  • 11
  • 73
  • 112
  • 3
    I'd use a different name to avoid confusion with `std::any_of` (which btw could also be used here) – 463035818_is_not_an_ai Jul 27 '20 at 16:14
  • @idclev463035818 How would you use std::any_of? By setting up a container first, or do I miss something here? – ypnos Jul 27 '20 at 16:15
  • 2
    @idclev463035818 Actually, I think `any_of` is the right name. `std::any_of` should always be qualified anyway. And using `std::any_of` is not ideal, because a separate range would need to be created. – cigien Jul 27 '20 at 16:17
  • @ypnos yes setting up a container first similar to your answer. Actually I am not certain if I'd prefer `any_of` or `count`. `count` could be misunderstood to do more than necessary, but of course for a set the count is either `0` or `1` – 463035818_is_not_an_ai Jul 27 '20 at 16:17
  • 1
    OP asks for a "more efficient" way while the any_of in this answer is doing the same as baseline, only easier to the eyes. I still like the answer as I believe the quest to "more efficiency" here is a lost cause. However, using std::any_of with a vector is certainly less efficient than using a set. – ypnos Jul 27 '20 at 16:19
  • "should always be qualified" not sure of that, I could imagine that one wants to allow for ADL to kick in. Anyhow point taken, you already had my vote – 463035818_is_not_an_ai Jul 27 '20 at 16:20
  • 1
    @idclev463035818 Added a caveat to put this into a namespace anyway. The name is too good to change I feel :) – cigien Jul 27 '20 at 16:21