0

I am trying to use an if statement to check if a user inputted day of the week (3-digit string i.e "Fri") is equal to a weekday. The user inputted variable is stored in "dayOfWeek"

I wanted to do this with the following code:

if (dayOfWeek == ("Mon"|| "Tue" || "Wed" || "Thu" || "Fri")){
}

I was hoping this code would read as "if day of the week is equal to "Mon" or "Tue ... etc" so that the if statement would return true for any of these inputs

I then received the following error message:

Invalid operands to binary expression ('std::string' (aka 'basic_string') and 'bool')

I understand that one possible fix is to repeat "dayOfWeek ==" for each input, but this does not seem intuitive and would create a very long if statement.

Is my approach not possible or is there a better approach?

Flann3l
  • 69
  • 5
  • If you really want to use an if statement, there have to be multiple complete comparisons. Like `if( (dayOfWeek=="Mon") || (dayOfWeek=="Tue))` ... and so on. You tie multiple strings together with boolean operators, which means, the strings will be implicitly converted to bools before being or'ed. – kaba Mar 17 '22 at 21:51
  • TLDR: no, your approach is not possible, C++'s syntax and grammar simply doesn't work like that. – Sam Varshavchik Mar 17 '22 at 21:53
  • 1
    You could equate the day of the week string to a day number via some lookup, like a `std::unordered_map` and then use simple math on it, like `if (dayNum >= 1 && dayNum <= 5)`. Even better would be to replace the magic 1 & 5 with meaningful enum names. You can also just write the long chain of `||` and hide it in a function never to be typed again. – Retired Ninja Mar 17 '22 at 21:55
  • Anything that looks cleaner is probably going to be a lot slower. Ninja has the right of it. Do it the long-winded way and hide the sucker in a function you can call like `if (is day_of_week(day))` – user4581301 Mar 17 '22 at 22:06
  • Why are you looking for the 'most efficient way'? Do you mean in terms of speed? - because for this kind of thing, speed (within reason) doesn't usually matter. – Paul Sanders Mar 17 '22 at 22:53
  • I've reopened this, because there are quite a few techniques that apply to this (matching strings) that don't apply to the other question it was closed as duplicating (which is about variables in general, not strings). – Jerry Coffin Mar 18 '22 at 00:14
  • @JerryCoffin I just noticed your comment, and the fact that you'd reopened this question. That's a good point, an answer that is specifically suited to strings doesn't really apply on the original target. I've added a canonical for that to the target list. Your answer looks quite interesting, you should go ahead and post it on that target (and delete it from under this question.) – cigien Mar 18 '22 at 03:03

3 Answers3

1

sadly c++ isnt English, as you found out. You need

if (dayOfWeek == "Mon"|| 
  dayOfWeek == "Tue" ||
  dayOfWeek == "Wed" ||
  dayOfWeek == "Thu" ||
  dayOfWeek == "Fri"){
}

You ask for the most efficient - I missed that.

This is it for sure.

Is it the most 'elegant' and 'flexible'? - no

The answer with the set is certainly elegant and flexible, but for me it obscures the intent, especially if the set def is distant from the check code

pm100
  • 48,078
  • 23
  • 82
  • 145
  • C++ is more like math where you have to be stupidly exact about what you want. Think it's worth showing how to use a `set` to do this? Slower for this little data, but more in line with their expectations. – user4581301 Mar 17 '22 at 21:53
  • @user4581301 I would never use a set for this, it would only obscure the intent – pm100 Mar 17 '22 at 21:54
  • I wouldn't either, but more because of speed. The older approach with iterators is a mess, but as of C++20 `weekdays.contains(dayOfWeek)` is reasonably readable – user4581301 Mar 17 '22 at 21:57
1

With a little bit of other work, we can make almost the desired syntax work, and work reasonably efficiently as well.

We start by defining a string_set type to hold the set of strings we're going to search in:

class string_set {
    std::vector<std::string_view> strings;
public:
    string_set(std::string_view s) { strings.push_back(s); }

    string_set& operator+(std::string_view s) { strings.push_back(s); return *this; }
    string_set& operator+(string_set const &other) {
        std::copy(other.strings.begin(), other.strings.end(), std::back_inserter(strings)); 
        return *this;
    }

    string_set& operator||(string_set const &other) { return *this  + other; }
    string_set& operator||(std::string_view other) { return *this + other;}

    friend bool operator==(std::string_view s, string_set const &ss ) {
        return std::any_of(ss.strings.begin(), ss.strings.end(),
            [&](std::string_view t) { return s == t; }
        );
    }
};

Then we define a user-defined literal operator to create one of those from a string:

// uses implicit conversion from `string_view` to `string_set`.
// Some may prefer to do that explicitly.
string_set operator"" _ss(char const *s, std::size_t len) { return std::string_view(s, len); }

With these in place, we can create a string_set and test for membership like this: if (input == "Mon"_ss + "Tue" + "Wed" + "Thu" + "Fri"). If you prefer to use || instead of +, you can do that, but since its precedence is lower, you need to enclose the strings in parentheses: if (input == ("Mon"_ss || "Tue" || "Wed" || "Thu" || "Fri")).

The == operator does a linear search in the strings we've given. This wouldn't be a good idea if there were a lot of strings, but this is intended to be used as above, with a set of strings specified directly in the if statement, so we're optimizing for small sets--think of something like a dozen or so as an upper bound.

Of course, if we really wanted something that worked more efficiently for a lot of strings, we could do that too. For example, instead of a vector of string_view and a linear search, we could build a trie, and search that. This would tend to optimize search speed at the expense of more time building the trie to start with, so if we did that we'd probably want to (for one possibility) make the building phase constexpr, so it could/would all happen at compile time. At least in my opinion, for a first cut at things, this would be pretty serious overkill though.

Since it uses std::string_view, this does require C++ 17 or higher.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
0

Here there is an example

#include<iostream>
#include<vector>
#include <set>
#include <string>

int main()
{
    std::set<std::string> container = {
            "Mon",
            "Tue" ,
            "Wed" ,
            "Thu" ,
            "Fri"
    };
    std::cout<<(container.find("Mon") != container.end());
}

If you want more stuff about sets check the this guide

Hope it will be helpful

P.Carlino
  • 661
  • 5
  • 21