mynameisdlo
mynameisdlo

Reputation: 589

How can I better check whether two char variables are in some set of values?

Recently, our professor has requested that we use two char variables (day) to receive the input from the user.

The code below works fine as a check to ensure that either Mo, Tu, We, Th, Fr, Sa, Su are the only two characters which are entered together as a pair. If anything else is received as input, it'll loop and ask the user for valid input.

The input should be case-insensitive, meaning that, for example, "mO" and "tu" are acceptable. It seems like there is a lot of repetition that is happening. Is there a way to clean this up?

cout << "Please enter the day of the week did you made the long distance call (Mo Tu We Th Fr Sa Su): ";
cin >> dayOne >> dayTwo;

while ((dayOne != 'M' && dayOne != 'm' || dayTwo != 'O' && dayTwo != 'o') &&
       (dayOne != 'T' && dayOne != 't' || dayTwo != 'U' && dayTwo != 'u') &&
       (dayOne != 'W' && dayOne != 'w' || dayTwo != 'e' && dayTwo != 'E') &&
       (dayOne != 'T' && dayOne != 't' || dayOne != 'H' && dayTwo != 'h') &&
       (dayOne != 'F' && dayOne != 'f' || dayTwo != 'R' && dayTwo != 'r') &&
       (dayOne != 'S' && dayOne != 's' || dayTwo != 'A' && dayTwo != 'a') &&
       (dayOne != 'S' && dayOne != 's' || dayTwo != 'U' && dayTwo != 'u'))
{
    cin.clear();
    cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    cout << endl << "You have entered an invalid day. Please re-enter a day in the correct format (Mo Tu We Th Fr Sa Su): ";
    cin >> dayOne >> dayTwo;
}

Upvotes: 41

Views: 2858

Answers (6)

tucuxi
tucuxi

Reputation: 17955

I would first convert inputs to lowercase, which cuts on the amount of possible combinations. Then I would solve it with a single if-statement per day:

// returns 0-6 for valid days, -1 for invalid ones
int dayOfWeek(char a, char b) {
    a = tolower(a); // requires #include <cctype>
    b = tolower(b);
    if (a == 'm' && b == 'o') return 0;
    // 5 more here
    if (a == 's' && b == 'u') return 6;
    return -1;      // date was invalid
}

And then I would use it as @PaulEvans suggested:

cout << "Please enter the day of the week did you made the long distance call (Mo Tu We Th Fr Sa Su): ";
cin >> dayOne >> dayTwo;

int day = -1;

while ((day = dayOfWeek(dayOne, dayTwo)) == -1)
{
    cin.clear();
    cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    cout << endl << "You have entered an invalid day. Please re-enter a day in the correct format (Mo Tu We Th Fr Sa Su): ";
    cin >> dayOne >> dayTwo;
}

// day is 0 for monday, ... 6 for sunday

Upvotes: 2

Paul Evans
Paul Evans

Reputation: 27577

Don't know if you're using/allowed regexes, but I'd solve it like this:

bool isDayOfTheWeek(char a, char b)
{
    std::string day({a, b});
    std::regex pattern("Mo|Tu|We|Th|Fr|Sa|Su", std::regex_constants::icase);
    return std::regex_search(day, pattern);
}

Then simply:

cout << "Please enter the day of the week did you made the long distance call (Mo Tu We Th Fr Sa Su): ";
cin >> dayOne >> dayTwo;

while (!isDayOfTheWeek(dayOne, dayTwo))
{
    cin.clear();
    cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    cout << endl << "You have entered an invalid day. Please re-enter a day in the correct format (Mo Tu We Th Fr Sa Su): ";
    cin >> dayOne >> dayTwo;
}

Upvotes: 6

crsn
crsn

Reputation: 609

Another approach that might be worth mention is to organize your data, so that you can use std functions against it (std::find)

// Example program
#include <algorithm>
#include <string>
#include <vector>
#include <iostream>

int main()
{
    const std::vector<std::string> days = {
        "mo", "tu", "we", "th", "fr", "sa", "su"
    };

    bool found = false;

    while (found == false) {
        char dayOne, dayTwo;
        std::cout << "Please enter the first letter of the day" << std::endl;
        std::cin >> dayOne;
        std::cout << "Please enter the second letter of the day" << std::endl;
        std::cin >> dayTwo;

        std::string fullDay;
        fullDay += std::tolower(dayOne);
        fullDay += std::tolower(dayTwo);

        found = std::find(days.begin(), days.end(), fullDay) != days.end();
        std::cout << (found ? "correct day " : "invalid day, please try again ")
                  << fullDay
                  << std::endl;
    }
}

run it here

Upvotes: 13

Harjit Singh
Harjit Singh

Reputation: 75

How about

switch (256 * tolower(dayOne) + tolower(dayTwo))
{
    case 256 * 'm' + 'o':
        // Monday
    case 256 * 't' + 'u':
        // Tuesday
}

and so on?

Upvotes: 8

cigien
cigien

Reputation: 60268

You could write a fold-expression that compares 2 characters to a string:

template<typename ...Days>
bool any_of(char a, char b, Days ...days)
{
    return (... || (a == days[0] && b == days[1]));
}

and then use it like this:

while (! any_of(std::tolower(dayOne), std::tolower(dayTwo), "mo", "tu", "we", "th", "fr", "sa", "su"))
    // keep asking for input

Here's a demo.

This should satisfy the requirement of using 2 char inputs.

Upvotes: 41

anatolyg
anatolyg

Reputation: 28290

You typically use tolower or toupper to convert your char variable to the correct case first. I like using tolower - it looks marginally better.

dayOne = tolower(dayOne);
dayTwo = tolower(dayTwo);

while (
    (dayOne != 'm' || dayTwo != 'o') &&
    (dayOne != 't' || dayTwo != 'u') &&
    (dayOne != 'w' || dayTwo != 'e') &&
    (dayOne != 't' || dayTwo != 'h') &&
    (dayOne != 'f' || dayTwo != 'r') &&
    (dayOne != 's' || dayTwo != 'a') &&
    (dayOne != 's' || dayTwo != 'u'))
{
    ...
}

You can further change it by using memcmp to compare both characters at once, but I am not sure it would simplify the code.

Upvotes: 21

Related Questions