tags:

views:

336

answers:

10

I have 20 digits that I would like to associate with strings. Is there a faster way besides using a switch case I can do this with. Basically I need to convert an int to a correponding string and the numberes arent necessarily packed. Maybe there is something in Qt as well?

for example 1 would be used to get "Request System Info" 2: "Change System Info" 10: "Unkown Error"

A: 

std::map


Reading through your question again, I think you're saying that you have only 20 numbers to map (you said 20 digits, which made me think of very large numbers). If these are all in a fairly small range, you might be better off just using an array. You need an array of string pointers as large as largestIndex - smallestIndex + 1. Then to get the string associated with a certain number, you'd do something like:

std::string GetStatus(int statusID){
    return statusArray[statusID - smallestIndex];
}

The statusArray variable is initialized with something like:

void SetStatus(int statusID, std::string description){
    statusArray[statusID - smallestIndex] = description;
}

void InitStatuses(){
    statusArray = new std::string[largestIndex - smallestIndex + 1];
    SetStatus(1, "Request System Info");
    SetStatus(2, "Change System Info");
    SetStatus(10, "Unknown Error");
}

This would be faster than a map, and pretty easy to use. It's just not appropriate if your IDs vary widely.

P Daddy
+16  A: 

I recommend std::map<>

#include <map>
#include <string>

std::map<int, std::string> mapping;

// Initialize the map
mapping.insert(std::make_pair(1, "Request System Info"));
mapping.insert(std::make_pair(2, "Change System Info"));
mapping.insert(std::make_pair(10, "Unkown Error"));

// Use the map
std::map<int, std::string>::const_iterator iter =
    mapping.find(num);
if (iter != mapping.end())
{
    // iter->second contains your string
    // iter->first contains the number you just looked up
}

If you have a compiler that implements the initalizer-list feature of the draft C++0x standard, you combine your definition and initialization of the map:

std::map<int, std::string> mapping = {{1, "Request System Info"},
                                      {2, "Change System Info"}
                                      {10, "Unkown Error"}};

std::map<> scales well to a large number of entries as std::map<>::find runs in O(log N). Once you have the hash-map feature of the draft C++0x standard, you can easily convert this to a std::unordered_map<> which should be able to look things up in O(1) time.

R Samuel Klatchko
is this windows specific programming?
yan bellavance
@yan - std::map is part of the standard C++ library so it will be available with any compliant compiler.
R Samuel Klatchko
No, that's the STL.
Chris
k thx I will try it.
yan bellavance
+1 to the STL map, I was going to suggest it but you beat me to it ;)
John Ewart
IMO, the insert(make_pair) idiom in this example is harder to read than @pm100's example of mymap[key]=value. Strictly speaking the insert(make_pair) idiom might be slightly faster since the use of the bracket operator leads to a default constructor invocation, but I wonder if the performance boost is worth the readability benefits.
netjeff
Chris, the STL is part of the Standard C++ Library. Netjeff, if the object being copied around is heavyweight (such as std::string), the performance gain of using std::map::insert instead of std::map::operator[] can be substantial. Also, I don't think any C++ programmer is going to look at the calls to map::insert and not know what's going on...
Billy ONeal
at this point I think its a question of context and preference. this will be used for error messages which rarely occur so the performance issue in my case is almost not important. Anyways PM needs the points more than you do ;)
yan bellavance
+1  A: 

Something like: ?

#include <sstream>
using namespace std;
string f(int i)
{
    ostringstream oss;
    oss << i;
    return oss.str();
}
Marsh Ray
+3  A: 

If you are looking for speed, the switch statement will be the most efficient. Most C/C++ compilers will implement it as a binary tree, so it is really fast.

Peter Olsson
20 dense values might end up in a jump table - none of your maps and trees can cope with two jumps...
dionadar
also seems simpler to implement
yan bellavance
I was pretty sure a jump table would of been implemented but does that make it less efficient than std::map?
yan bellavance
+6  A: 

Qt also provides it's own map implementations - QMap and QHash.

QMap<int, QString> myMap;
myMap[1234] = "Some value";
myMap[5678] = "Another value";

or

myMap.insert(1234, "Some value");

The documentation gives more examples but it's very easy to use.

Rob
thx, since I am making a Qt app i will use your approach but my question is a bit more C++ in general so I will have to accept another answer...hope you dont mind (I wish I could accept 2 answers :D)
yan bellavance
Glad to be of help. :)
Rob
+2  A: 

If your numbers and strings are constant, a map will not work; it has has to be loaded.

For constant numbers and strings, I recommend an array of:

struct Entry
{
   unsigned int  key;
   const char *  text;
};

And for a large quantity, use a binary search algorithm; otherwise a linear search is about as fast (as long as the array is sorted).

Thomas Matthews
+3  A: 

easier way to use map

std::map<int, std::string> mymap
mymap[1] = "foo";
mymap[10] = "bar;
....
std::string lookup = mymap[idx];
pm100
and of course the implementation is at liberty to implement map however it likes, including as a switches, jump tables, etc (although it almost certain will be as a binary tree)
pm100
Pm100, the standard mandates some sort of binary tree structure. The standard explicitly forbids jump tables and the like.Also, while inserting, you will have better performance if you use std::map::insert instead of std::map::operator[].
Billy ONeal
One problem with using operator[] to do the lookup is it will modify your map if the index you are looking up is not already in the map.
R Samuel Klatchko
my bad billy, i must admit to not having read the standard
pm100
A: 

Are the strings known at compile time? If so create a map

std::map messages;

messages[key] = "some value"; messages[key2] = "some other value";

etc

If they're to be read from a file then something like this

std::ifstream ifile("the file")

while (ifile && !ifile.eof())
{
  ifile >> key >> value;
  messages[key] = value;
}

etc

Liz Albin
yes the strings are known at compile time
yan bellavance
A: 

If your numbers are contiguous (or nearly so), a simple array or vector of strings will work quite nicely.

If there are large gaps between the numbers, chances are that the fastest method will be an array of std::pair<int, std::string> (or std::pair<int, char *>). While most people automatically think of a binary search for fast lookups, in this case the small number of possibilities to be considered may well allow a linear search to compete quite effectively -- in fact, it may well be the fastest choice available. The reason is simple: while it'll do more comparisons, it avoids things like divisions, and comparisons on 20 ints is going to be fast anyway.

Edit: note that std::map probably is not a really good choice. std::map normally uses a balanced binary tree, which means 20 items will use 20 nodes, each of which is normally allocated separately. Each node will typically include at least two pointers in addition to the int you need to store, so you're roughly tripling the amount of data you're storing -- that all adds up to relatively poor cache locality. Given the small number of items involved, one cache miss will cost more than 20 comparisons.

Jerry Coffin
+2  A: 

If your strings are known at compile-time, then you can do this in C:

#include <stdio.h>

struct message {
    int val;
    const char *msg;
};

int main(void)
{
    struct message messages[] = {
        {1, "Request System Info"},
        {2, "Change System Info"},
        {10, "Unkown Error"}
    };
    size_t nmessages = sizeof messages / sizeof messages[0];
    size_t i;

    for (i=0; i < nmessages; ++i) {
        printf("%d : '%s'\n", messages[i].val, messages[i].msg);
    }
    return 0;
}
Alok
actually this is not what I need
yan bellavance
That's not sad, if the number of error codes is small and known at compile time. Of course, for anything more complicated, this simple scheme will be less than optimal.
Alok
@yan: can you explain more? I believe that you're building a table of err codes and error messages, right?
Alok
I needed to be able to map the index i that you are using to the message directly i.e. I am receiving a status variable from a function call and I want to insert the associated definition in a message without looking up every time.
yan bellavance
I needed to be able to map the index i that you are using to the message directly i.e. I am receiving a status variable from a function call and I want to insert the associated definition in a message without looking up every time or using a switch statement, STL map worked fine (dont mind him he's a meany).
yan bellavance
Fair enough. Thanks for the response.
Alok