1

I need a way to assign numbers to letters in C++, for example, '$' would represent the number 1. I obviously need to be able to obtain the number from the character with something like a function, e.g. getNumFromChar('$') would return 1 and getNumFromChar('#') would return 2. Is there an easy and fast way to do this in C++?

ocket8888
  • 1,060
  • 12
  • 31
Mashpoe
  • 504
  • 1
  • 9
  • 27

8 Answers8

6

The fastest way is to write a 256 entry lookup table, containing the mapped values in the character's ASCII index. This is similar to how isdigit and tolower work, for example:

int getNumFromChar(char c)
{
    static const int table[256] = {/* table entries */};

    return table[c & 0xff];
}
owacoder
  • 4,815
  • 20
  • 47
3

If you would like to assign the values yourself use a map and store your key to letter combinations. If you are ok with preassigned unique values mapped to each letter, and are only using ASCII characters, then type cast them to integers... ex) std::static_cast< int >('$');

2

Create a vector std::vector<int> v(256,0); which is indexed by your characters and initially all of their numbers are zeros that you could treat as invalid numbers. Finally assign for each 'numbered' character some number e.g. v['$'] = 1; v['#'] = 2; using a fact that characters are actually integers from 0 to 255.

Mikhail Volskiy
  • 209
  • 2
  • 5
1

As pointed out in the comments, you can use a std::map in the following way:

#include <iostream>
#include <map>
#include <cstring>

struct myComp
{
    bool operator()(const char* s1, const char* s2) const
    {
    return strcmp(s1, s2) < 0;
    }
};

int main()
{
    std::map<const char*, int, myComp> test;

    test["$"] = 1;
    test["#"] = 2;

    std::cout << "$ -> " << test["$"] <<"\n";
    std::cout << "# -> " << test["#"] <<"\n";

    return 0;
}

Live demo here.

Majority of the other answers will work only if you have a maximum of 256 values to be stored. However, using Maps, you can store just any number of elements.

abhishek_naik
  • 1,287
  • 2
  • 15
  • 27
  • And if you know that your key space is at most 256 entries large, a map is probably the slowest possible solution there is. – MikeMB May 25 '16 at 08:02
  • @MikeMB, I agree, but I believe that a Map lookup would be preferable to a Vector lookup (as suggested by Mikhail Volskiy above). And the basis of my belief is [this](http://stackoverflow.com/questions/6985572/which-is-the-fastest-stl-container-for-find). The OP is not using `std::find` per se, but he is *looking up* nonetheless. And besides, the OP has nowhere mentioned that he is working only with 256 values. – abhishek_naik May 25 '16 at 08:36
  • `std::find` and indexed based lookup are two completely unrelated operations. `std::find` is always O(n), whereas indexed base lookup for arrays and vectors is constant time (and in fact compiles down to something. like 1-3 instructions). More importantly, `std::map` is a caching and pipelining nightmare and (on modern processors) this often has a much higher performance impact than algorithmic complexity. If you are interested performance of operations on different data structures, I can recommend [this talk](https://www.youtube.com/watch?v=fHNmRkzxHWs) as a starting point. – MikeMB May 25 '16 at 22:03
  • The 256 Characters come from the assumption that the OP actually wants to use ASCII chars instead of strings. If that is not the case, a std::vector might potentially still be better than a `std::map`, but not necessarily. – MikeMB May 25 '16 at 22:09
0

A lot of people are suggesting std::map<char,int>, which is fine and works, but a faster (?) way of doing this with no dependencies is to just use a massive switch statement

int getNumFromChar(char c){
    switch(c){
        case '$':
            return 1;
        case '#':
            return 2;
        //etc
    }
    return -1; //just in case of error, for style points
}

Depending on how much you care about performance/memory usage and how many case statements you'd have to write, this may or may not be a viable way to do what you want. Just thought I'd throw it out there since at the time of this writing I don't believe anyone has.

EDIT: Also, depending on the frequency of use of each individual character, and if you know the entire mapping before using this function or if you ever change the mapping, a std::map is way better, but I believe this is faster otherwise.

ocket8888
  • 1,060
  • 12
  • 31
0

You could do something like this:

#include <map>
#include <iostream>
#include <exception>

typedef std::map<char, int> easymap_type;

class EasyMap {
public:
    EasyMap() {}
    virtual ~EasyMap() {}

    void assign_int_to_char(const int& i, const char& c)
    {
        _map[c] = i;
    }

    int get_int_from_char(const char& c) const
    {
        easymap_type::const_iterator it = _map.find(c);
        if (it == _map.end())
        {
            std::cerr << "EasyMap Error: uninitialized key - '" << c << "'" << std::endl;
            throw std::exception();
        }
        return it->second;
    }

private:
    easymap_type _map;
};

int main()
{

    EasyMap ezmap;

    ezmap.assign_int_to_char(42, 'a');

    std::cout << "EasyMap[a] = " << ezmap.get_int_from_char('a') << std::endl;
    std::cout << "EasyMap[b] = " << ezmap.get_int_from_char('b') << std::endl;

    return 0;
}

I handled an uninitizialized key by throwing an exception, but you could do it different ways.

vincent
  • 1,370
  • 2
  • 13
  • 29
0

If your compiler support c++11 feature,you can use std::unordered_map as container to store char and double like std::unordered_map<char,double>. Unordered map is an associative container that contains key-value pairs with unique keys. Search, insertion, and removal of elements have average constant-time complexity.In your problem char is the key and double is your value,char-double must be the key-value stored in container.

TongChen
  • 1,414
  • 1
  • 11
  • 21
0

There are already a lot of reasonable answers... I prefer the static_cast<int>('#')

And there always has to be the most stupid useless compile time template idea about how to solve a problem.

I know it's stupid, and I'm not good at this kind of things, but here is my shot at it in c++11. Don't take me seriously. I just had to do something dumb.

#include <string>
#include <array>
#include <utility>
#include <iostream>

constexpr uint  kNbChars {3};

constexpr std::array<std::pair<char, int>, kNbChars> kCharToInt {
      std::make_pair('$', 1)
    , std::make_pair('#', 2)
    , std::make_pair('@', 3)
};

template <char c>
int getInt()
{
    for (auto pair : kCharToInt)
    {
        if (pair.first == c)
        { return pair.second; }
    }
    return -1;
}

int     main()
{
    std::cout << getInt<'#'>() << std::endl;
    std::cout << getInt<'g'>() << std::endl;
}

I think you can make getInt() constexpr too in c++14, but I may be wrong and cannot check it right now.

Of course it really is useless since you have to know the letter at compile time, but you could work around that by, well, just not making getInt a template function...

vianney
  • 161
  • 6