This question already has an answer here:

Just instead of :

if  ( ch == 'A' || ch == 'B' || ch == 'C' || .....

for example, to do it like :

if  ( ch == 'A', 'B', 'C', ...

is there even a shorter way to summarize conditions ?

share|improve this question

marked as duplicate by Olaf, 1201ProgramAlarm, Hong Ooi, BlueRaja - Danny Pflughoeft, zespri 50 mins ago

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

4  
No, there's no shorthand for this. – Barmar 13 hours ago
12  
Maybe you want isupper(ch)? – Barmar 13 hours ago
9  
If your values are consecutive as shown, if (ch >= 'A' && ch <= 'C') or wherever it goes to. – Weather Vane 13 hours ago
5  
is this c++ or c? because there's a shorthand-ish way in c++ even if the things you're checking against aren't contiguous. – jaggedSpire 13 hours ago
3  
@EOF The C standard does require 'A' < 'B' < ... < 'Z' and 'a' < 'b' < ... < 'z', but indeed does not require contiguity, nor does it specify the ordering of 'A' and 'a'. The ordering requirement is not explicitly stated, but can be derived from the combination of the rules for the basic execution character set and the rules for the "C" locale. – zwol 13 hours ago

11 Answers 11

up vote 48 down vote accepted

strchr() can be used to see if the character is in a list.

const char* list = "ABCXZ";
if (strchr(list, ch)) {
  // 'ch' is 'A', 'B', 'C', 'X', or 'Z'
}
share|improve this answer
4  
This one solved my problem , thanks a lot mate – OptimusMaximus 13 hours ago
    
Maybe that is personal style, but I would eliminate the intermediate list variable and directly pass the string as an argument. – MikeMB 11 hours ago
9  
@monkeyman79 using a switch statement rather than a search through a sequence strikes me as a likely premature optimization. Certainly do it if the test is a bottleneck, but it's rather unlikely to be one, and switch statements are verbose enough to break up the flow of the surrounding code. – jaggedSpire 10 hours ago
1  
@jaggedSpire Ok. Agreed, but... yeah, you're right. – monkeyman79 9 hours ago
4  
@monkeyman79 You waiting tens of seconds for a reaction to a button isn't because people wrote code like this. It's because, when it was time to actually optimize, people didn't find the real bottlenecks correctly. Also, "awful response" was a bit harsh; the OP wasn't even asking about a performance bottleneck centered around this code. This is a 100% fine answer. – Jason C 6 hours ago

In this case you could use a switch:

switch (ch) {
case 'A':
case 'B':
case 'C':
    // do something
    break;
case 'D':
case 'E':
case 'F':
    // do something else
    break;
...
}

Note that this won't work as you might expect because of the use of the comma operator:

if  ( ch == 'A', 'B', 'C', ...

This first compares ch to 'A' and then discards the result. Then 'B' is evaluated and discarded, then 'C', and so forth until the last comma expression is evaluated as used as the value of the conditional.

share|improve this answer

templates!

template<class X, class Y>
bool in(X const& x, Y const& y)
{
    return x == y;
}

template<class X, class Y, class...Rest>
bool in(X const& x, Y const& y, Rest const&...rest)
{
    return in(x, y) or in(x, rest...);
}

int main()
{
    int ch = 6;
    std::cout << in(ch, 1,2,3,4,5,6,7) << std::endl;

    std::string foo = "foo";
    std::cout << in(foo, "bar", "foo", "baz") << std::endl;

    std::cout << in(foo, "bar", "baz") << std::endl;
}
share|improve this answer
    
Thanks for your help but i don't really understand the whole code because i am not so deep into programming and this is c++ – OptimusMaximus 13 hours ago
2  
This is nice, but looks like a huge overkill if only char comparsion in needed. – HolyBlackCat 12 hours ago
17  
Deep down inside, everybody wants to write code in LISP. – Pete Becker 12 hours ago
1  
Deep down inside, everybody wants to wear the paint off their angle bracket keys. – Jason C 6 hours ago
    
@HolyBlackCat: The important point is the same code can be reused the next time you want to do "is the thing in the list" too. – Hurkyl 5 hours ago

If you need to check a character against an arbitrary set of characters, you could try writing this:

std::set<char> allowed_chars = {'A', 'B', 'C', 'D', 'E', 'G', 'Q', '7', 'z'};
if(allowed_chars.find(ch) != allowed_chars.end()) {
    /*...*/
}
share|improve this answer
2  
a static const set maybe? – Laurent LA RIZZA 12 hours ago
    
Unless you are dealing with very, very big character sets (more than 256), that must be the slowest possible way to do this. – MikeMB 11 hours ago
1  
@MikeMB Well, it's probably faster than strchr, and it's obvious in code what it's doing without comments. AND it extends better for runtime determination of which characters are allowed. The only real disadvantage is probably increased memory use. – Xirema 11 hours ago
2  
@Xirema: Why do you think it is faster than strchr? And why would the strchr version require comments? – MikeMB 10 hours ago
1  
@Xirema: I very much doubt that asymptotic complexity matters here and std::set is a pretty inefficient data structure for storing characters (Chandler expained this imho quite well on cppcon14: youtube.com/watch?v=fHNmRkzxHWs). If you have time you might want to have a look at the my pseudo-"benchmark": ideone.com/h6WxBI. On my machine strchr outperforms the std::set solution by a factor of 2 or more, but that is probably highly system, compiler and benchmark specific. It is actually not as bad as I expected. – MikeMB 8 hours ago

The X-Y answer on the vast majority of modern systems is don't bother.

You can take advantage of the fact that practically every character encoding used today stores the alphabet in one sequentially-ordered contiguous block. A is followed by B, B is followed by C, etc... on to Z. This allows you to do simple math tricks on letters to convert the letter to a number. For example the letter C minus the letter A , 'C' - 'A', is 2, the distance between c and a.

Some character sets, EBCDIC was discussed in the comments above, are not sequential or contiguous for reasons that are out of scope for discussion here. They are rare, but occasionally you will find one. When you do... Well, most of the other answers here provide suitable solutions.

We can use this to make a mapping of letter values to letters with a simple array:

//                    a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p, q,r,s,t,u,v,w,x,y, z
int lettervalues[] = {1,3,3,2,1,4,2,4,1,8,5,1,3,1,1,3,10,1,1,1,1,4,4,8,4,10};

So 'c' - 'a' is 2 and lettervalues[2] will result in 3, the letter value of C.

No if statements or conditional logic required what-so-ever. All the debugging you need to do is proof reading lettervalues to make sure you entered the correct values.

As you study more in C++, you will learn that lettervalues should be static (current translation unit-only access) and const (cannot be changed), possibly constexpr (cannot be changed and fixed at compile time). If you don't know what I'm talking about, don't worry. You'll cover all three later. If not, google them. All are very useful tools.

Using this array could be as simple as

int ComputeWordScore(std::string in)
{
    int score = 0;
    for (char ch: in) // for all characters in string
    {
        score += lettervalues[ch - 'a'];
    }
    return score;
}

But this has two fatal blind spots:

The first is capital letters. Sorry Ayn Rand, but 'A' is not 'a', and 'A'-'a' is not zero. This can be solved by using std::tolower or std::toupper to convert all input to a known case.

int ComputeWordScore(std::string in)
{
    int score = 0;
    for (char ch: in) // for all characters in string
    {
        score += lettervalues[std::tolower(ch) - 'a'];
    }
    return score;
}

The other is input of characters that aren't letters. For example, '1'. 'a' - '1' will result in an array index that is not in the array. This is bad. If you're lucky your program will crash, but anything could happen, including looking as though your program works. Read up on Undefined Behaviour for more.

Fortunately this also has a simple fix: Only compute the score for good input. You can test for valid alphabet characters with std::isalpha.

int ComputeWordScore(std::string in)
{
    int score = 0;
    for (char ch: in) // for all characters in string
    {
        if (std::isalpha(ch))
        {
            score += lettervalues[std::tolower(ch) - 'a'];
        }
        else
        {
            // do something that makes sense here. 
        }
    }
    return score;
}

My something else would be return -1;. -1 is an impossible word score, so anyone who calls ComputeWordScore can test for -1 and reject the user's input. What they do with it is not ComputeWordScore's problem. Generally the stupider you can make a function, the better, and errors should be handled by the closest piece of code that has all the information needed to make a decision. In this case, whatever read in the string would likely be tasked with deciding what to do with bad strings and ComputeWordScore can keep on computing word scores.

share|improve this answer

Similarly to the C strchr answer, In C++ you can construct a string and check the character against its contents:

#include <string>
...
std::string("ABCDEFGIKZ").find(c) != std::string::npos;

The above will return true for 'F' and 'Z' but false for 'z' or 'O'. This code does not assume contiguous representation of characters.

This works because std::string::find returns std::string::npos when it can't find the character in the string.

Live on Coliru

Edit:

There's another C++ method which doesn't involve dynamic allocation, but does involve an even longer piece of code:

#include <algorithm>
...
char const chars[] = "ABCDEFGIKZ";
return std::find(std::begin(chars), std::end(chars), c) != std::end(chars);

This works similarly to the first code snippet: std::find searches the given range and returns a specific value if the item isn't found. Here, said specific value is the range's end.

Live on Coliru

share|improve this answer
    
It is telling, that the c++-ish version is a lot longer and possibly requires a dynamic memory alllocation to do the same thing as the c-style version. Luckily most of the c-standard library is also part of the c++ standard library. – MikeMB 11 hours ago
    
Indeed. One could also do a std::find on the same static array as the C version, which would eliminate the dynamic allocation at the price of increased length. – jaggedSpire 11 hours ago
    
@MikeMB added the amusingly verbose alternative without dynamic allocation – jaggedSpire 11 hours ago
    
@Downvoter care to explain? – jaggedSpire 10 hours ago
    
I'm really looking forward to the day when one can use things like std::string_view and ranges in SO answers ;) – MikeMB 10 hours ago

There is solution to your problem, not in language but in coding practices - Refactoring.

I'm quite sure that readers will find this answer very unorthodox, but - Refactoring can, and is used often to, hide a messy piece of code behind a method call. That method can be cleaned later or it can be left as it is.

You can create the following method:

private bool characterIsValid(char ch) {
    return (ch == 'A' || ch == 'B' || ch == 'C' || ..... );
}

and then this method can be called in a short form as:

if (characterIsValid(ch)) ...

Reuse that method with so many checks and only returning a boolean, anywhere.

share|improve this answer

Yet another answer on this overly-answered question, which I'm just including for completeness. Between all of the answers here you should find something that works in your application.

So another option is a lookup table:

// On initialization:
bool isAcceptable[256] = { false };
isAcceptable[(unsigned char)'A'] = true;
isAcceptable[(unsigned char)'B'] = true;
isAccetpable[(unsigned char)'C'] = true;

// When you want to check:
char c = ...;
if (isAcceptable[(unsigned char)c]) {
   // it's 'A', 'B', or 'C'.
}

Scoff at the C-style static casts if you must, but they do get the job done. I suppose you could use an std::vector<bool> if arrays keep you up at night. You can also use types besides bool. But you get the idea.

Obviously this becomes cumbersome with e.g. wchar_t, and virtually unusable with multibyte encodings. But for your char example, or for anything that lends itself to a lookup table, it'll do. YMMV.

share|improve this answer
1  
Might be worth an edit to slip in a mention of std::map for those sparse, multibyte cases. – user4581301 3 hours ago

For this specific case you can use the fact that char is an integer and test for a range:

if(ch >= 'A' && ch <= 'C')
{
    ...
}

But in general this is not possible unfortunately. If you want to compress your code just use a boolean function

if(compare_char(ch))
{
    ...
}
share|improve this answer
1  
Please state explicitly the assumptions that this code makes. It is not portable. – Pete Becker 13 hours ago
1  
That first test will not work if the range of characters is larger and you're working with EBCDIC, not ASCII. – PaulMcKenzie 13 hours ago
1  
@PaulMcKenzie If there's z/OS developers reading this question, we've got bigger problems. – Jason C 6 hours ago

If you want to check for all capital alphabets then you can use if(ch>='A' && ch<='Z').

For all small letters if(ch>='a' && ch<='z').

And for all alphabets input you can use if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')).

share|improve this answer
1  
Please state explicitly the assumptions that this code makes. It is not portable. – Pete Becker 13 hours ago

One option is the unordered_set. Put the characters of interest into the set. Then just check the count of the character in question:

#include <iostream>
#include <unordered_set>

using namespace std;

int main() {
  unordered_set<char> characters;
  characters.insert('A');
  characters.insert('B');
  characters.insert('C');
  // ...

  if (characters.count('A')) {
    cout << "found" << endl;
  } else {
    cout << "not found" << endl;
  }

  return 0;
}
share|improve this answer

Not the answer you're looking for? Browse other questions tagged or ask your own question.