Hey, I was wondering if its possible to swap the key values and the mapped values of a std::map around? I had hopes that the map::swap() function would do this, but it turns out it just swaps the content of two different maps with the same type.
Hey, I was wondering if its possible to swap the key values and the mapped values of a std::map around? I had hopes that the map::swap() function would do this, but it turns out it just swaps the content of two different maps with the same type.
As in given a value you want to find the corresponding key(s)? That is certainly possible, but it is slow as you basically have to perform a linear search of the values to obtain the corresponding keys.
As such, what you can do is to maintain another map (or multimap) that maps in the other direction, or use something like Boost.Bimap.
Not really. My idea is to call a function to retrieve a map containing the same content as another map, only the key values and the mapped values have been swapped. I don't think that's the same thing you're saying, but I could be wrong...
I suppose I could write my own, if there's not something already available for that.
Yeah, it is not quite what I thought you were talking about, but Boost.Bimap effectively solves your problem.
Thanks, I'll check it out.
On second thought, a non-STL library function is probably not the best idea under the circumstances, seeing as the method needs to be in a generated file of my program.
I guess I'll just write a global templated function called swap() expecting any type of map, and returning any type of map, to file. The code should be easy enough...
Well, it's your prerogative, but I urge everyone to download and, at the very least, try boost.
You should be aware that std::map has four template parameters: the key type, the mapped type, the key comparator type, and the allocator type. A given standard library implementation might add more, though I am not absolutely sure if that is allowed. Furthermore, a std::map has unique keys, but duplicate values are allowed. If you want to reverse a map with duplicate values, you then have the problem where one of the entries must be discarded unless you return a std::multimap instead.Quote:
Originally Posted by Programmer_P
Boost.Bimap is a header-only library. You can use bcp to extract a portion of Boost to distribute with your program.Quote:
Originally Posted by Programmer_P
Would this function declaration work:
?Code:template <typename map aMapType> aMapType swap(aMapType& aMap);
It is not even syntactically valid. You should address the duplicate value problem that I talked about first. Furthermore, swap may be a poor name for this.
Oh well...
Nevermind the templated function then.
I wrote one for a specific map type instead.
And I have no idea what you mean by a "duplicate value problem", btw.
Consider a map where you map 1 -> 3 and 2 -> 3. Now, you want to turn the keys into the mapped values and the mapped values into the keys. You apply your function, and thus obtain 3 -> 1 and 3 -> 2. But a map can only have unique keys, thus one of these two mappings must be discarded, unless you use a multimap instead.Quote:
Originally Posted by Programmer_P
Anyway, this is what I tried:
Note that it will not work if the standard library implementation introduces more template parameters than those outlined in the C++ standard.Code:#include <map>
#include <utility>
template<typename Key, typename T,
typename SourceCompare, typename SourceAllocator,
typename ResultCompare, typename ResultAllocator>
void flip(const std::map<Key, T, SourceCompare, SourceAllocator>& source,
std::multimap<T, Key, ResultCompare, ResultAllocator>& result)
{
typedef std::map<Key, T, SourceCompare, SourceAllocator> source_type;
typedef typename source_type::const_iterator source_iterator;
const source_iterator end = source.end();
for (source_iterator i = source.begin(); i != end; ++i)
{
result.insert(std::make_pair(i->second, i->first));
}
}
int main()
{
std::map<int, long> x;
std::multimap<long, int> y;
flip(x, y);
}
Oh, right. I see what you mean.
Fortunately, though, my function will only (theoretically, assuming of course it is used correctly) swap a specific map (one that does not have any duplicate values, every value will be unique).
Interesting. I will look over your code when I get the time.Quote:
Anyway, this is what I tried:
Note that it will not work if the standard library implementation introduces more template parameters than those outlined in the C++ standard.Code:#include <map>
#include <utility>
template<typename Key, typename T,
typename SourceCompare, typename SourceAllocator,
typename ResultCompare, typename ResultAllocator>
void flip(const std::map<Key, T, SourceCompare, SourceAllocator>& source,
std::multimap<T, Key, ResultCompare, ResultAllocator>& result)
{
typedef std::map<Key, T, SourceCompare, SourceAllocator> source_type;
typedef typename source_type::const_iterator source_iterator;
const source_iterator end = source.end();
for (source_iterator i = source.begin(); i != end; ++i)
{
result.insert(std::make_pair(i->second, i->first));
}
}
int main()
{
std::map<int, long> x;
std::multimap<long, int> y;
flip(x, y);
}
Have to go do something in the real world right now...
A map of vectors would fix the problem laserlight mentioned.
A map or multimap is designed to be accessed via keys and not by values. Why would you ever need to key off of a value in a map?Code:class DontDoThis
{
public:
typedef std::map<std::string,IWhatever *> objectMap;
typedef objectMap::iterator objectMapIter;
typedef std::vector<objectMapIter> mapIterVector;
typedef keyVector::iterator mapIterVectorIter;
typedef std::map<IWhatever *,mapIterVector> reverseLookupMap;
typedef reverseLookupMap::iterator reverseLookupMapIter;
...
...
private:
objectMap m_objectMap;
reverseLookUpMap m_reverseLookupMap;
...
...
};
There are some cases where it would be useful to swap a map around, so you can get the values which were originally the keys with the values which were originally the mapped values.
For example, I added a map to the generated file produced by ConvertEnumToStrings which has a type like this:
This map stores the string names of the enumerations found in an enum, as well as their corresponding numerical values.Code:map <string, unsigned int> enumerationsMap;
Normally you would want to retrieve the numerical values of the enumerations by using their string names, but sometimes you might want to retrieve the string names of the enumerations by their corresponding numerical values instead.
I wanted to provide both methods for convenience, though I still have the function for retrieving a string name of an enumeration by passing in an enumeration name or its numerical value as well.
That way my generated class for an enum found is far more flexible and lets you do more stuff. I started coding more on my web coder program, and realized I needed a map generated by ConvertEnumToStrings to retrieve a unsigned int value based on the string name of an enumeration. So I now have produced a new version of ConvertEnumToStrings which does just that.
EDIT: And I realize I could always create a different map for that as a class member like the other map, but thought, what the heck. A function for swapping maps around would be useful, I think, though I guess if I'm going to write such a function, I might as well write it so it works with any map which doesn't have duplicates values in it.
I don't think it's weird to want to key off a value, altho in years of using map like structures I can't recall doing it very often.
There are apparently some data structures for doing this, such as the UB tree, but I've never written one.
WRT to retrieving a string with a number, you can just use a vector<string> in the same order as the enum, and use the index to look up the string.
Nevermind. I see why you used the multimap. Because it allows duplicate values in the map.
@laserlight: And don't you think it would be more fail-proof if I write the function to pass in just a map, and return the multimap, instead of passing in both?
This way there is no risk of someone passing in a multimap that already contains stuff. Remember the objective is to get an exact copy of one map, only the key values and the mapped values have been swapped.
Yes, but you cannot get the string with the number that way (well, you can, see below). I'm just saying, you might as well use a parallel vector in this case since the numbers are from an enum (0,1,2,3,4...). Generally, you would need another map, as laserlight recommends, or to write some specialized data structure.
Of course, if you do not want to duplicate the data that way (which may not be a good idea) and the enums are only a few hundred items you might as well just write a function to iterate thru the map and find them -- you are looking for a number, which is a pretty cheap compare, the look up is not efficient but in this case it will be very quick anyway.
Not all enums have values going from 0 - lastEnumerationValue, incrementing by 1 each enumeration. I wanted to handle all cases of enums (or at least those with explicit numbers assigned to the enumerations, and not using variable names).
There is no need to write a function to iterate through a map to find a number. All you have to do to access a specific enumeration number will be:Quote:
Of course, if you do not want to duplicate the data that way (which may not be a good idea) and the enums are only a few hundred items you might as well just write a function to iterate thru the map and find them -- you are looking for a number, which is a pretty cheap compare, the look up is not efficient but in this case it will be very quick anyway.
The above of course assumes you have already ran ConvertEnumToStrings on whatever file that has that code in it. The result of that would be:Code:#include "results.h" //include the generated file created by ConvertEnumToStrings
using namespace std;
enum {
aEnumeration1 = 0,
aEnumeration2 = 1,
aEnumeration3 = 3
//etc...
};
int main() {
CnameofenumToStrings object; //create an object of the generated class for the enum
map <string, unsigned int> enumerationsMap = object.getEnumerationsMap();
unsigned int valueOfAEnumeration1 = enumerationsMap.at("aEnumeration1");
multimap <unsigned int, string> swappedMap = swap(enumerationsMap);
cout<< "The numerical value of the enumeration with the string name of \"" << swappedMap.at(0) << "\"is:\n"
<< valueOfAEnumeration1 <<endl;
cout<< "\nPress Enter to end this program." <<endl;
cin.get();
return 0;
}
The numerical value of the enumeration with the string name of "aEnumeration1" is:
0
Or you could do any number of things with it. That was just a small example.
Sigh. If the number is the value and not the key, yes you do. What would you call all that code you just posted? Is swap a function, or what? What does "iterating through the map" mean? Some code, ie. probably a function?* Or does it occur by magic?
My point was that you could do it in a simpler way if the values were sequential, but as you indicate that may not always be the case, so you would need a function to generate a second map, which is what you have now.
* its a basic principle of programming that you put sections of code you are going to reuse into a function (instead of say, cutting and pasting them all over the place).
I just demonstrated swapping the key values and the mapped values around, which is what the topic of this thread is all about.
Yes, swap() will be a function in the generated file created by ConvertEnumToStrings once I post the new version here. Basically, it will use a map reference parameter and you can pass any kind of map to it. The function will then iterate through the map, inserting the key values into the map values of a new map, and vice versa. It will then return the new map.Quote:
What would you call all that code you just posted? Is swap a function, or what? My point was that you could do it in a simpler way if the values were sequential, but as you indicate that may not always be the case, so you would need a function to generate a second map, which is what you have now.
But I'm still not iterating through the map to find a number (at least not in the sense that I will have to code for this). That is the point I'm trying to make.
You can just use the at() function to retrieve an enumeration's number.
Here is the code for the swap() function (based off of laserlight's example, and untested so far):
Code:template<typename Key, typename T, typename SourceCompare, typename SourceAllocator>
multimap<Key, T, ResultCompare, ResultAllocator>
swap(map<Key, T, SourceCompare, SourceAllocator>& sourceMap) {
typedef map<Key, T, SourceCompare, SourceAllocator> sourceType;
typedef typename sourceType::const_iterator sourceIterator;
const sourceIterator end = sourceMap.end();
multimap<Key, T, SourceCompare, SourceAllocator> newMap;
for (sourceIterator i = sourceMap.begin(); i != end; i++) {
newMap.insert(make_pair(i->second, i->first));
}
return newMap;
}
No, but if you generate that second map with swap every time you do a look up, it is very inefficient. You should only do it once.
However, that's not possible if the first map is bound to change (I don't think it is in your case tho) -- you would have to keep regenerating the second map, again a big waste of time. In that case, considering again that these maps are quite small (a few hundred or a thousand items), you might as well just do the iteration. IMO, since they are small, that's what you might as well do anyway, there really is not much need for a second map here.
But that's just an opinion. The way you have it now is fine as long as you don't have to do that more than once.
What would be the point of calling the swap function more than once for a single map?
Of course I would only do it once...
The map wont change, unless you do some screwing around with the contents of the map (which is HIGHLY unrecommended :p). The map will be generated once for every enum in a file you pass to ConvertEnumToStrings. A separate class with functions for the whole works will be created for each enum in a single file, so there's no problem there.Quote:
However, that's not possible if the first map is bound to change (I don't think it is in your case tho) -- you would have to keep regenerating the second map, again a big waste of time.
One line of code versus several lines of code is definitely the way to go, IMO... ;)Quote:
In that case, considering again that these maps are quite small (a few hundred or a thousand items), you might as well just do the iteration. IMO, since they are small, that's what you might as well do anyway, there really is not much need for a second map here.
Yep, like already mentioned, you will only have to do it once for a single enum.Quote:
But that's just an opinion. The way you have it now is fine as long as you don't have to do that more than once.
Then you can retrieve any enumeration's string name using the same swapped map.
Oh, and I need to mention that the swap() function will be global and not a member function, and will be created only once for a single file, regardless of whether or not there are multiple enums in the file passed to ConvertEnumToStrings.
This is why I said write a function. Then it is only one line of code.
Considering the maps are const, you should keep the method you are using. Unfortunately in C++ you can't make use of void*, or otherwise dynamically type the value in a map, or what I would do would be to add a value to the original map with a unique string key like "myprog_index_map" with the value pointing to the second map. This makes keeping the relationship between them simple.Code:map<string, int> data; //the map
string name = getstringfromint(data, 16); // example call
But you can't do that, you might want to make this map into a class which contains both of them, then you can manipulate a single object with both maps in it. ;)
What?!
It is already one line of code:
Code:multimap<unsigned int, string> swappedMap = swap(enumerationsMap);
You're doing something different there, obviously...Quote:
Code:map<string, int> data; //the map
string name = getstringfromint(data, 16); // example call
Creating a map with strings as key values, and ints as mapped values, then calling a "getstringfromint" funtion? Btw, that's 2 lines... :p
Not sure if I'm getting you there...Quote:
Considering the maps are const, you should keep the method you are using. Unfortunately in C++ you can't make use of void*, or otherwise dynamically type the value in a map, or what I would do would be to add a value to the original map with a unique string key like "myprog_index_map" with the value pointing to the second map. This makes keeping the relationship between them simple.
What would be the point of adding such a value to the original map?
And how would you point it at the second map?
I already have a class which contains the original map. You then call classObject.getEnumerationsMap() to get a copy of that map. Like I was saying earlier, I could easily create the second map as a member of the same class, and then add another function for retrieving that map, but I wrote the swap function so I can swap any map around, in case I need such a thing later on, I will have already written it.Quote:
But you can't do that, you might want to make this map into a class which contains both of them, then you can manipulate a single object with both maps in it. ;)
The purpose of the swap function is to be able to easily swap the key values and the mapped values around in any kind of map passed to the function, and it will then return a multimap containing all that stuff. That can be accomplished in one line of code like I just demonstrated.
I don't know who you directed that question at, but I'll assume its me:
Why would I want to make the maps static (and no, the maps are not currently static)? What advantage would the 'static' keyword give me in this case?
True. I hate editing though. :pQuote:
Programmer_P: There is a thing called the edit button. Use it. Posting thrice is a row is considered bad on many forums.
It makes me look stupid, because it looks like I didn't complete the thought the first time around (and I did, I just had a new thought, hence the new post).
haha.
Yes, because it is a function call just like that other one.
No, the map declaration was for clarity, that would be the original map. So "getstringfromint" would be a function to iterate through the map values to find the correct key. That would be in the case where you cannot use the two maps because the first one is not const (or effectively const).Quote:
You're doing something different there, obviously...
Creating a map with strings as key values, and ints as mapped values, then calling a "getstringfromint" funtion? Btw, that's 2 lines... :p
Otherwise, how are you to keep the relationship between them? In your example program, there are only two so it is simple. However, in a program containing several mapped enums generated at runtime, unless you want to keep regenerating the parallel map (which we've already recognized as silly), you need something to "group" those variables. Namely the class of which the map is a part.Quote:
What would be the point of adding such a value to the original map?
And how would you point it at the second map?
Ah, well you should definitely make the second map a member of the class then. The swap() function does not have to be part of the class in order to be used by it. Probably you can leave the second map uninitialized in case it is not needed.Quote:
I already have a class which contains the original map. You then call classObject.getEnumerationsMap() to get a copy of that map. Like I was saying earlier, I could easily create the second map as a member of the same class, and then add another function for retrieving that map, but I wrote the swap function so I can swap any map around, in case I need such a thing later on, I will have already written it.
Yes, I understand all that.Quote:
The purpose of the swap function is to be able to easily swap the key values and the mapped values around in any kind of map passed to the function, and it will then return a multimap containing all that stuff. That can be accomplished in one line of code like I just demonstrated.
My point was you could not add a member to a map<string,int> named "index_map" which points to another object (the inverted map), at least as far as I know. You could get away with it on a 32-bit system but obviously that is an ungood idea. Perhaps you could use a template or something instead of an int as the value, I have not tried that; otherwise you must wrap them both in something.
Something like this???
Code:std::map< std::string, std::tuple<int, std::map<int, std::string>*> > MyMap;
Since you only need one such pointer per map (and not one for each element), that would be kind of a waste. You could do this I guess:
Then you could set an element to an int (corresponding to the enum) OR use them as special pointers. This is still a bit of a waste on 64-bit.Code:typedef union _val val;
union _val {
int n;
std::map<string,val> *p;
};
In C++ a class, or at least a struct wrapper, would be better/more orthodox methinks.
Yep...so basically, your post didn't make sense, since you were saying "write a function, and it will be one line of code", and I had already did so. :p
Yeah, I figured as much.Quote:
No, the map declaration was for clarity, that would be the original map.
Yes, but what would be the point of that, if you use the swap() function and you already have another map with the same content as the original, only the key values and the mapped values have been swapped? You can just pass the numerical value of the enumeration you want to retrieve the name of to at(), which is easy enough.Quote:
So "getstringfromint" would be a function to iterate through the map values to find the correct key.
Neither one of them are const (at least not in my example). A copy of the original map contained in the class generated by my program was gotten by calling the classObject.getEnumerationsMap() function, which admittedly returns a const& to a map<string, unsigned int>, but you could not have known that by looking at the code I posted. And anyway, the variable that the content of the referenced map was copied into at the function call, was not prefixed by the 'const' keyword, hence it should not be const. And when I created a multimap called "swappedMap" and copied the return value of the function swap, which returns a multimap by value, into swappedMap, it still was not const.Quote:
That would be in the case where you cannot use the two maps because the first one is not const (or effectively const).
Sorry, but that is flawed reasoning, IMO. First of all, say a given file had 5 enums in it (each one named something differently, with completely different enumerations, names and all, though it really wouldn't make a difference if something was the same as something else) and you ran ConvertEnumToStrings on said file. Now you have a generated file (most of the time, I use "results.h" as the filename) containing a separate class for each enum found in the file. So you're looking at 5 different classes in the generated file. Now, when you want to get something of a particular enum, you first include the results.h file in your program:Quote:
Otherwise, how are you to keep the relationship between them? In your example program, there are only two so it is simple. However, in a program containing several mapped enums generated at runtime, unless you want to keep regenerating the parallel map (which we've already recognized as silly), you need something to "group" those variables. Namely the class of which the map is a part.
(Note that the results file is always created inside the same directory as the file passed to ConvertEnumToStrings)Code:#include "results.h"
Now you create an object of a class by the name of CnameofenumyouwanttodosomethingwithToStrings like this:
Ok, now you can get the map for that particular enum by doing:Code:CnameofenumyouwanttodosomethingwithToStrings classObject;
Now, you can use the map in your program for whatever you need the enum for, by getting the numerical value of the enumeration you're interested in by just doing:Code:map <string, unsigned int> enumerationsMap = classObject.getEnumerationsMap();
You now have the relevant numerical value of the enumeration you're interested in, so you can use it for whatever you want (for example, passing it to a function expecting an enumeration of the said enum).Code:unsigned int numValue = enumerationsMap.at("enumerationname");
Ok, now say you want the string name of the same enumeration. So, you do:
You now have a multimap containing the same content as the enumerationsMap, only the key values and the mapped values have been swapped. So you can now access a specific enumeration's string name by doing something like:Code:multimap <unsigned int, string> swappedMap = swap(enumerationsMap);
Now you can do whatever you want with that. Ok, now let's say you want to do something similiar with some of your other 4 enums. You will NOT use the same "classObject" object to get a map of another enum, since that is only an object of the first enum you're doing stuff with. You will create an object of the relevant class for that enum.Code:string enumerationName = swappedMap.at(0);
And I see no problem with "regenerating the parallel map" as you put it, though in reality each enum which you chose to swap its map for would have its own relevant map and relevant swapped map.
As for the swap() function, that is not a member of any of the classes. Like already mentioned, it is global, and only defined once in the results file generated by ConvertEnumToStrings.Quote:
Ah, well you should definitely make the second map a member of the class then. The swap() function does not have to be part of the class in order to be used by it. Probably you can leave the second map uninitialized in case it is not needed.
Yes, I understand all that.
And like I already said, I noted from the start that I could make a second map as a member of the relevant class. But I chose not to, since I think having a swap function would be cool, and it would useful for any map I want to do that with. Anyways, its not likely you would want to swap the map around that often. And I provided another method for getting the name of an enumeration: getEnumerationName()
As I said clearly quite a few times, I was describing an alternative to that, and then you kept asking me questions about it, so I continued to describe it. Obviously you would not use both methods.
Sorry, I mean effectively "const", as in, are not changed after they are created. This is essential to the value of your method.Quote:
Neither one of them are const (at least not in my example).
So if the programmer using this interface wants to use several such maps in a program at once, you are leaving the burden on them to create a "swappedMap" for each map, and somehow keep track of this relationship.Quote:
Sorry, but that is flawed reasoning, IMO. [...] Now, you can use the map in your program for whatever you need the enum for
Let's say I have 3 different mapped enums. I create a swapped map for each of them:
Do you not see how it would be (much, much) better to be able to refer to this?Code:multimap <unsigned int, string> swappedMap1 = swap(enumerationsMap1);
multimap <unsigned int, string> swappedMap2 = swap(enumerationsMap2);
multimap <unsigned int, string> swappedMap3 = swap(enumerationsMap3);
This means the programmer does not have to number the variables in parallel (a very weak an inflexible method) or come up with there own derived class or a typedef like this:Code:enumerationsMap1.swappedMap;
Which is what I would do if the class interface did not provide another means. Having to just remember which swappedMap is for what is only a minor hassle, what makes it a genuine problem is that is also limits what you can do with them -- for example, if you pass the map in as a variable to a function, you will have to pass both maps. Stuff like that is just a bad development practice -- you want to create options, not curtail them. At a certain point you will hit something this inflexibility will render impossible, meaning the programmer will have to create his/her own improptu interface, as above.Code:typedef struct {
map <string, unsigned int> enumerationsMap;
multimap <unsigned int, string> swappedMap;
} emaps;
Yes, as I said I do understand what you are doing: for the second time, a function does not need to be part of a class for the class to use that function.Quote:
As for the swap() function, that is not a member of any of the classes. Like already mentioned, it is global, and only defined once in the results file generated by ConvertEnumToStrings.
Yes, you are definitely missing the point here:
The idea is to forsee the possibility. As a programmer, I'm just letting you know about the possibilities I see here, and the difficulties that will ensue if you don't forsee them.Quote:
Originally Posted by Mk27
I don't recall you saying "quite clearly" that it was an alternative any time, but maybe that's just me...
But yeah, I understand that its an alternative. I was just questioning if it was in fact better than my method.
Yeah, true. Like I already said, you should never try to modify either one of the maps (the original or the swapped). And anyone with any sense wouldn't, either. Why do it? There is no point.Quote:
Sorry, I mean effectively "const", as in, are not changed after they are created. This is essential to the value of your method.
You already have a map for the enum. If you need a map for something else, create a different map. Its that simple.
Only for those maps they want to swap (which I doubt would be that often anyway). But yeah, that's the idea...Quote:
So if the programmer using this interface wants to use several such maps in a program at once, you are leaving the burden on them to create a "swappedMap" for each map, and somehow keep track of this relationship.
And anyway, I already provide other methods for getting the string names of the enumerations:
andCode:const vector<string>& getEnumerationNames();
You can get any enumeration's name you want with the second method. And the first method returns a vector of the string names of ALL enumerations, so you can also do something with that as well.Code:const string& getEnumerationName(nameofenum enumeration);
Actually, it would probably be something like:Quote:
Let's say I have 3 different mapped enums. I create a swapped map for each of them:
Do you not see how it would be better to be able to refer to this:Code:multimap <unsigned int, string> swappedMap1 = swap(enumerationsMap1);
multimap <unsigned int, string> swappedMap2 = swap(enumerationsMap2);
multimap <unsigned int, string> swappedMap3 = swap(enumerationsMap3);
Code:enumerationsMap1.swappedMap;
if I were going to do it that way. But yeah, the syntax might be a little better using the method you demonstrated. But both methods would work.Code:enumerationsMap1.getSwappedMap();
And unless I see significant justification for a swapped version of the original enumerationsMap as a member of each enum's class, there's no reason to add that functionality in.
Ok, yes, I see what you're saying. You seem to think that adding both maps (original and swapped) to a struct has some advantage over just keeping them both as a their map types, and doing stuff with them directly. I don't really see the point to such an approach.Quote:
This means the programmer does not have to number the variables in parallel (a very weak an inflexible method) or come up with there own derived class or a typedef like this:
Which is what I would do if the class interface did not provide another means.Code:typedef struct {
map <string, unsigned int> enumerationsMap;
multimap <unsigned int, string> swappedMap;
} emaps;
Either way, you still end up having to access a member of the map to do something useful with the map (i.e. retrieving a string name of an enumeration referred to by an unsigned int). And if I were coding and doing something like that with multiple enums, I'd think I would just write the code in a logical order, i.e.
as opposed to:Code://map the first enum
//if you need to, swap the map
//use the swapped map for something
//map the second enum
//if you need to, swap the map
//use the swapped map for something
orCode://map the first enum
//if you need to, swap the map
//add both maps to a struct
//create an object of the struct
//use the structObject.originalMap for something
//use the structObject.swappedMap for something
Code://get the map of the first enum
//get the swapped map of the first enum
//if you need to, use the swapped map for something
Exactly. I thought you were saying that the swap() function itself was a member function.Quote:
Yes, as I said I do understand what you are doing: for the second time, a function does not need to be part of a class for the class to use that function.
Not really. I don't see why you would need to pass both maps to a function.
You would have to give me an example of what the function would be for before I even consider that possibility. I just can't imagine a scenario where you would pass one map to a function, then pass a swapped map of that map to the same function...
I did create options:Quote:
Stuff like that is just a bad development practice -- you want to create options, not curtail them. At a certain point you will hit something this inflexibility will render impossible, meaning the programmer will have to create his/her own improptu interface, as above.
Sorry, but I can just not imagine why you would need anything other than those functions for dealing with the enumeration names and their associated numerical values. swap() is a luxury actually. The main reason I added it in to begin with, was in case I needed to swap other maps than the enumeration maps. The other reason was for the rare times when someone might want to swap the enumerations map around, for whatever reason.Code:const vector<string>& getEnumerationNames();
const string& getNameOfEnum();
const int& getNumOfEnumerations();
const string& getEnumerationName(nameofenum enumeration);
const map<string, unsigned int> getEnumerationsMap();
Fair enough -- as I said after years of using map-like data structures, in reality it's very rare that you would need a swapped map at all. However, if you see a need for it, then it is strange that you would think someone would not want to write a function to use it.
This misses the point of object oriented programming, but nevermind.Quote:
I did create options:
I have already written a function: swap()
All those functions I mentioned (with the exception of swap(), which is global) are members of the generated enum's class.Quote:
This misses the point of object oriented programming, but nevermind.
But admittedly, the generated class(es) are not OOP-oriented, since you would only need to create one object of each class. On the other hand though, I don't think OOP is the best answer for everything, nor was it the best answer for my generated file either.
You could, but then you either make some assumptions about the comparator type and allocator type of the multimap, or make life a little more difficult for the caller (or make life more difficult for yourself by having to provide overloads). When the multimap is an argument, you can deduce these automatically.Quote:
Originally Posted by Programmer_P
See the code in this post, then tell me what you think.
I think that you should try to actually use the function template. You will get compile errors that can be fixed, and in fixing them you will agree with me.Quote:
Originally Posted by Programmer_P
Oh yeah...my bad. I forgot to change "ResultCompare" to "SourceCompare" and "ResultAllocator" to "SourceAllocator" on the multimap line.
I fixed that, but now a different problem cropped up:
C++ code - 31 lines - codepad
Right, but you cannot change "ResultCompare" to "SourceCompare" and "ResultAllocator" to "SourceAllocator" because they are not equivalent. For example, if you are mapping std::string to int, then the SourceCompare comparator would be used to compare std::string objects, so how can it be used to compare int objects?
Oh, I know what the problem is...
I'm trying to use the map paramaters as the multimap parameters, which obviously wont work, because apparently the compiler can't convert them to that.
I should be able to fix that in a sec.
It's just as easy to create a second map that maps values to a vector of iterators that point into the first map. As long as you correctly maintain both maps during removes and inserts it works fine and it's less confusing and if you do it correctly there is hardly any additional overhead until you want to remove a value which might mean you need to remove one or more entries in the original map.
However, that being said if you need key off of values instead of keys or you need to do both and I would say there is a serious flaw in your design. Multimaps were designed to map unique keys to one or more values and maps were designed to map unique keys to one unique value. That is the intent of the data structure. Altering that design is not good and points to a flaw in your own design or to the idea that maps may not be what you need.
Most of these types of problems can easily be solved by re-design and/or by making use of multiple STL containers. If you understand the STL you can build the system so it adds minimal overhead at the cost of a bit of memory. However the memory that would be saved in another approach does not justify the convoluted code and logic it requires to accomplish. When you run into situations like this it is often best to use the approach that makes the code the clearest.
Ok, I did it:
http://codepad.org/OdGVPiEW
Side note: I hate maps and multimaps. The syntax sucks. I guess the inserting part is easy enough, but its a real pain to iterate through a map, and retrieve stuff. Why can't they just provide a getKeyValue(index) and a getMappedValue(index) function, or something?
Maybe I'll write my own...
No you shouldn't. STL code is well tested, standard, maintained by professionals, and widely known and used. IE: If you use it almost any C++ programmer in the world will understand your code. If you use it you can be assured the code works and is, for the most part, free of bugs. Re-implementing this would cost you unnecessary time and effort and you would be fixing bugs in it from here till eternity. Just use STL. That is what it is there for. There are times when it may be necessary for time critical code to code your own container but those cases are the exception and not the rule.Quote:
Maybe I'll write my own...
Once you get used to the iterator paradigm of the STL you will learn to love it and will wonder how you ever programmed without it. It really is your best friend. I highly recommend after using it for a bit you purchase Effective STL by Scott Meyers. I would also recommend the gang of four Design Patterns book by Erich Gamma, Richard Helm, Raph Johnson, and John Vlissides. Neither of these books leaves my sight when programming.
So you have made "some assumptions about the comparator type and allocator type of the multimap" and also of the map ;)Quote:
Originally Posted by Programmer_P
Iterating through a map to retrieve stuff and asking for getKeyValue() sounds like you are not using the appropriate container. getMappedValue() already exists as the find() member function coupled with iterator dereference, or as the overloaded operator[].Quote:
Originally Posted by Programmer_P
I have no idea what you mean by that. Could you post some example code for that?
Ok...Quote:
As long as you correctly maintain both maps during removes and inserts it works fine and it's less confusing and if you do it correctly there is hardly any additional overhead until you want to remove a value which might mean you need to remove one or more entries in the original map.
Interesting English. I think you meant:Quote:
However, that being said if you need key off of values instead of keys or you need to do both and I would say there is a serious flaw in your design.
In response to how I translated what you said, and not to what you actually technically said, I'd say that is a matter of opinion. I think there are times when you may have a map with some content, but what you really need is a map with the same content (of sorts), only the key values are the mapped values, and the mapped values are the key values. That's where my swap() function comes in handy.Quote:
However, that being said, if you need to get a key value from a mapped value, instead of a mapped value from a key value, or you need to do both (?; I think we can remove that part completely...), I would say there is a serious flaw in your design.
That is true, yet what we're talking about here, the container storing stuff doesn't start out as a multimap. It starts out as a map, hence each key will be mapped to just one value. It is only after using swap that it becomes a multimap. And now that I think of it, I don't think multimaps will be useful in this scenario at all. I'll go ahead and change it to a standard map.Quote:
Multimaps were designed to map unique keys to one or more values and maps were designed to map unique keys to one unique value.
Technically, I wasn't altering any design at all. I was only taking the content of one map, and putting it another map, only with the key values as the mapped values, and the mapped values as the key values in the new map.Quote:
That is the intent of the data structure. Altering that design is not good and points to a flaw in your own design or to the idea that maps may not be what you need.
That is not changing the underlying design of a map at all. It is only changing what is what in what, or something to that effect.
Maybe. But I'd need to see some of these "easy" solutions to this problem in action first before I decide whether that is really the best idea or not.Quote:
Most of these types of problems can easily be solved by re-design and/or by making use of multiple STL containers.
I'd say the code is pretty clear...Quote:
If you understand the STL you can build the system so it adds minimal overhead at the cost of a bit of memory. However the memory that would be saved in another approach does not justify the convoluted code and logic it requires to accomplish. When you run into situations like this it is often best to use the approach that makes the code the clearest.
Code:CnameofenumToStrings classObject;
map <string, unsigned int> enumerationsMap = classObject.getEnumerationsMap();
multimap <unsigned int, string> swappedMap = swap(enumerationsMap);
Not really. I decided to do away with the custom compare and allocator types altogether in the swap function, since it proved to be too difficult to get them into the function. I figure you can always add them in later after you get the returned map from swap(). As it is now, the returned map will end up with the default comparator and default allocator.
Why?Quote:
Iterating through a map to retrieve stuff and asking for getKeyValue() sounds like you are not using the appropriate container.
I figure you need to look at it like this:
You create a map.
You insert stuff into the map.
Now you want to retrieve stuff from the map.
So you call getMappedValue(getKeyValue(i)), or something like that, the basic idea being that you want to get the current key value you're at in the map, then use it to get the mapped value.
Yeah, but find() is such a pain since it returns an iterator for the map type instead of the mapped value itself, then you have to dereference the iterator to get at the current mapped element, creating problems because then you have to have an iterator iterator in a loop, then increment the iterator. And you have to pass it the key value you want to retrieve, meaning you will most of the time have to know what's actually stored in the key side of the map at the current index. Plus, when you create an iterator, you have to create one specifically for that map type. There is not a generic one for any map.Quote:
getMappedValue() already exists as the find() member function coupled with iterator dereference, or as the overloaded operator[].
As for the [] operator, I already tried outputting nameOfMap[i] in a for loop, but it wouldn't compile for some reason.
Maybe I'm griping, but I don't know...
I think the vector type is a lot more friendly than multimap (and possibly map) for the whole process of iterating through it and retrieving values.
Whether you use a map of vectors or a multi-map is up to you. The multi-map for the reverse look-up does indeed fit the bill. I guess my thinking here is to step back and think seriously about why it is you need to do this? What part of your design is forcing you to need to look for values instead of keys?
This is simply a reverse lookup. But normally you use a map b/c you want to assign some key or some type of ID to a value. If the same value is being placed into a map then theoretically it should have the same ID. That is where the confusion comes in. Two instances of a class usually means two unique keys or IDs in the map that point to two different instances. If you want to share the instance of an object then one unique ID or key is needed.Quote:
I think there are times when you may have a map with some content, but what you really need is a map with the same content (of sorts), only the key values are the mapped values, and the mapped values are the key values.
For instance:
Objects A B C D of type IMyObject
Container: std::map<unsigned int, IMyObject *>
key, value
0,A
1,B
2,C
3,D
4,E
5,A
6,B
7,C
8,D
9,E
I would argue here that 5,6,7,8 and 9 all point to different instances of A,B,C,D,E and therefore should have unique keys. If you want to share the instances of A,B,C,D,E then you need some way of either sharing their keys or making them available. A reverse lookup map or multi-map would work or you could return the ID from the function that did the insertion and then make that available to those objects that need it. I'm not used to seeing identical values stored in a map under a different key - that is, when the values are pointers.
I guess my reasoning is that if key 0 and key 5 have the same exact value you have now wasted memory by storing a shared object twice. Of course this only applies if you are storing pointers to objects in the map. But even if you map from int to int the key determines the uniqueness not the value.
1,1
2,3
4,1
5,2
6,2
So in this map from int to int if you want to get the value with a key of 1, it is 1. If you want the value with a key of 4, it is 1. Why would you ever care about all the values that were 1? Yes they both have a value of 1 but that really doesn't mean anything in the context of this map. Obviously some other object thought it was necessary to store a 1 with a key of 4 or it would not have inserted it into the map.
Oh, and does std::map actually have an at() function? It appears to because I've managed to compile my ConvertEnumToStrings program using it, but I can't find it documented anywhere on the web. And I know for sure std::multimap doesn't, because it wouldn't compile using at().
From MSDN for std::map:
Quote:
Reference
Constructors
map
Constructs a list of a specific size or with elements of a specific value or with a specific allocator or as a copy of some other map.
Typedefs
allocator_type
A type that represents the allocator class for the map object.
const_iterator
A type that provides a bidirectional iterator that can read a const element in the map.
const_pointer
A type that provides a pointer to a const element in a map.
const_reference
A type that provides a reference to a const element stored in a map for reading and performing const operations.
const_reverse_iterator
A type that provides a bidirectional iterator that can read any const element in the map.
difference_type
A signed integer type that can be used to represent the number of elements of a map in a range between elements pointed to by iterators.
iterator
A type that provides a bidirectional iterator that can read or modify any element in a map.
key_compare
A type that provides a function object that can compare two sort keys to determine the relative order of two elements in the map.
key_type
A type that describes the sort key object which constitutes each element of the map.
mapped_type
A type that represents the data type stored in a map.
pointer
A type that provides a pointer to a const element in a map.
reference
A type that provides a reference to an element stored in a map.
reverse_iterator
A type that provides a bidirectional iterator that can read or modify an element in a reversed map.
size_type
An unsigned integer type that can represent the number of elements in a map
value_type
A type that provides a function object that can compare two elements as sort keys to determine their relative order in the map.
Member Functions
begin
Returns an iterator addressing the first element in the map.
clear
Erases all the elements of a map.
count
Returns the number of elements in a map whose key matches a parameter-specified key.
empty
Tests if a map is empty.
end
Returns an iterator that addresses the location succeeding the last element in a map.
equal_range
Returns an iterator that addresses the location succeeding the last element in a map.
erase
Removes an element or a range of elements in a map from specified positions
find
Returns an iterator addressing the location of an element in a map that has a key equivalent to a specified key.
get_allocator
Returns a copy of the allocator object used to construct the map.
insert
Inserts an element or a range of elements into the map at a specified position.
key_comp
Retrieves a copy of the comparison object used to order keys in a map.
lower_bound
Returns an iterator to the first element in a map that with a key value that is equal to or greater than that of a specified key.
max_size
Returns the maximum length of the map.
rbegin
Returns an iterator addressing the first element in a reversed map.
rend
Returns an iterator that addresses the location succeeding the last element in a reversed map.
size
Returns the number of elements in the map.
swap
Exchanges the elements of two maps.
upper_bound
Returns an iterator to the first element in a map that with a key value that is greater than that of a specified key.
value_comp
Retrieves a copy of the comparison object used to order element values in a map.
Operators
operator[]
Inserts an element into a map with a specified key value.
Ok, so I think what you're basically saying is this:
std::maps may or may not have duplicate mapped values. Each one of those mapped values with the same value will all be "mapped" to the same key, correct?
So when we flip it around, so that the key values become the mapped values, and vice versa, we're now looking at a map (assuming of course it had duplicate mapped values) which has duplicate keys. I guess that is why laserlight used multimap instead of map in her example (I switched it back to use multimaps instead of maps). But I'm not even sure if multimaps even support duplicate keys or not or if they just support multiple mapped values to a single key.
So I guess that is a problem...
Right, I get what you're saying here. Though technically, they will be unique keys I think, just with the same values. The only way you would be able to tell duplicate keys apart is if you check the indexes. But is it even possible for a multimap to store duplicate key values? I see in the multimap reference, it mentions that it allows "different elements to have the same key value", but I think that means that it only allows different mapped values to have the same key value, and it doesn't mention if it also allows different keys with the same name to be mapped to the same mapped value.Quote:
For instance:
Objects A B C D of type IMyObject
Container: std::map<unsigned int, IMyObject *>
key, value
0,A
1,B
2,C
3,D
4,E
5,A
6,B
7,C
8,D
9,E
I would argue here that 5,6,7,8 and 9 all point to different instances of A,B,C,D,E and therefore should have unique keys.
Maybe I could add some functionality in the swap() (or flip(), whatever name I end up finally using) function to check for duplicates in the map that was passed before inserting into the multimap.
AFAIK, no one posted in this thread any identical mapped values that were pointers...Quote:
If you want to share the instances of A,B,C,D,E then you need some way of either sharing their keys or making them available. A reverse lookup map or multi-map would work or you could return the ID from the function that did the insertion and then make that available to those objects that need it. I'm not used to seeing identical values stored in a map under a different key - that is, when the values are pointers.
I guess here you're referring to the map and not the multimap, since all keys are unique, only there are duplicate mapped values, each one mapped to a different key.Quote:
I guess my reasoning is that if key 0 and key 5 have the same exact value you have now wasted memory by storing a shared object twice. Of course this only applies if you are storing pointers to objects in the map. But even if you map from int to int the key determines the uniqueness not the value.
1,1
2,3
4,1
5,2
6,2
So in this map from int to int if you want to get the value with a key of 1, it is 1.
However, after swapping that around, it would look like this:
1, 1
3, 2
1, 4
2, 5
6, 2
which brings us to the case where there are duplicate key values (though each one is mapped to a different mapped value), instead of duplicate mapped values. Can multimap handle this scenario? Also, what if the resulting multimap ended up something like this:
1, 1
3, 2
1, 1
2, 2
1, 1
Now there are both duplicate key values, and duplicate mapped values. And each one of the duplicate key values points at an identical mapped value which also has duplicates. What happens now? :p
If by "values" you mean "mapped values", then you might care about all the mapped values that are 1 if you were storing multiple instances of 1 in the mapped side of the map, for some reason. ;)Quote:
If you want the value with a key of 4, it is 1. Why would you ever care about all the values that were 1? Yes they both have a value of 1 but that really doesn't mean anything in the context of this map. Obviously some other object thought it was necessary to store a 1 with a key of 4 or it would not have inserted it into the map.
But anyway, I think this is all irrevelant. All I need to know is if multimap will support all cases of maps with duplicate mapped values. If so, then I will keep using it.
For this you would need a multi-map. I still do not understand why one would care about all the instances of a particular value in the map.Quote:
If by "values" you mean "mapped values", then you might care about all the mapped values that are 1 if you were storing multiple instances of 1 in the mapped side of the map, for some reason.
There is a misunderstanding here. A map requires 1 unique key per value. This does not mean you cannot store the exact same value with a different key. From the perspective of a map object this is perfectly valid. However, when you move to storing pointers to objects on the heap in the map you could run into problems. So let's say you were storing pointers to objects on the heap in the map.Quote:
Right, I get what you're saying here. Though technically, they will be unique keys I think, just with the same values. The only way you would be able to tell duplicate keys apart is if you check the indexes.
Given the following code:
You now have this:Code:typedef std::map<unsigned int,Object *> theMap;
typedef theMap::iterator theMapIter;
theMap m_theMap;
...
...
Object *pObject = new Object();
m_theMap.insert(theMap::value_type(0,pObject));
m_theMap.insert(theMap::value_type(1,pObject));
0, pointer to same pObject as pObject with key 1
1, pointer to same pObject as pObject with key 0
Perfectly valid from a map standpoint but dangerous. Essentially the same exact object can be accessed via two map keys or you have assigned two IDs to the same object. So if one object has a stored ID of 1 for pObject and one has a 0 for pObject now you have a mess. This could also potentially load the same object twice into memory if the map did any resource loading or other operations if the ID was not in the map. Also if you remove the object with key 1 and cleanup the object and remove it from the map the pointer with key 0 now points to garbage. For non-pointer maps this is not an issue but when you start storing pointers in your containers you could run into some ugly issues if you continue with this line of thinking.
Honestly, I don't really care either. :D
In fact, you were the one that brought that up first... :p
I said that only in response to you mentioning it, and I was really talking about storing the instances of 1 found in the mapped values side of the map in something outside the map.
I don't really give a damn at this current time, as a matter of fact, though.
Laserlight brought up the legit problem that if you do this you will have to remove all instances of value from the original map. This is where the 'all instances of values' comes from.
Yeah, a std::map does, but I thought you were talking about a std::multimap. A lot of the rest of that post was based off of that assumption...
Yeah, I understand the whole concept of duplicate mapped values (i.e. values with the same name) inside an std::map, each one of those mapped values having a different key.Quote:
This does not mean you cannot store the exact same value with a different key.
I think you meant:Quote:
From the perspective of a map object this is perfectly valid. However, when you move to storing pointers to objects on the heap in the map you could run into problems. So let's say you were storing pointers to objects on the heap in the map.
Given the following code:
Code:typedef std::map<unsigned int,Object *> theMap;
typedef theMap::iterator theMapIter;
That last couple of hours I've been fooling around with maps, and learned that you need to specify the type of the map when you access the "iterator" member of the map class. That is one of the annoyances I expressed in an earlier post.Code:typedef theMap<unsigned int, Object*>::iterator theMapIter;
I can't imagine a scenario where you would want to swap a map having pointers as the mapped values around in the first place. That makes no sense at all. You're going to get unsigned int values by using a pointer key??Quote:
You now have this:Code:theMap m_theMap;
...
...
Object *pObject = new Object();
m_theMap.insert(theMap::value_type(0,pObject));
m_theMap.insert(theMap::value_type(1,pObject));
0, pointer to same pObject as pObject with key 1
1, pointer to same pObject as pObject with key 0
Perfectly valid from a map standpoint but dangerous. Essentially the same exact object can be accessed via two map keys or you have assigned two IDs to the same object.
So if one object has a stored ID of 1 for pObject and one has a 0 for pObject now you have a mess.
This could also potentially load the same object twice into memory if the map did any resource loading or other operations if the ID was not in the map. Also if you remove the object with key 1 and cleanup the object and remove it from the map the pointer with key 0 now points to garbage. For non-pointer maps this is not an issue but when you start storing pointers in your containers you could run into some ugly issues if you continue with this line of thinking.
No, I think a better approach for this is to not touch the original map at all, but only insert a mapped value into a key value of another map if the same mapped value doesn't already exist in the other map, i.e. so I would call find() or something on the current mapped value to see if it already exists in the new map. If it does, I wont insert it. Otherwise, I will.
Ok, I added that in:
C++ code - 37 lines - codepad
That means that you have assumed that the default comparator and default allocator will be used. This is pretty much what I said :)Quote:
Originally Posted by Programmer_P
Oh, now I understand. i is an index, as in a vector. Therefore, you are using maps wrongly.Quote:
Originally Posted by Programmer_P
That is true, yet it is not true: one can generically declare an iterator.Quote:
Originally Posted by Programmer_P
Yes, and they will be used, since the map parameter of the function is expecting a map with only two explicit parameters (namely the key value and the mapped value). You would not be able to pass a map with a non-default comparator and/or a non-default allocator. Like I was saying though, if you needed a custom comparator and a custom allocator, for whatever reason, for that map, I think you could always just make another multimap, using the same key and mapped value types, and specifying the comparator and allocator you want to use, then iterating through the 2-parameter multimap, and copying the current elements into the 4-parameter multimap, so you end up with a nearly identical multimap as the swapped map, only it has a non-default comparator and a non-default allocator.
Who's using maps wrongly?Quote:
Oh, now I understand. i is an index, as in a vector. Therefore, you are using maps wrongly.
It was an example, and an example alone. I was really only trying to demonstrate other possible ways of getting mapped values and key values than those already provided, my point being that I thought the current methods kind of sucked.
Show the code then... ;)Quote:
That is true, yet it is not true: one can generically declare an iterator.
Well I'm out of this discussion. We have gone from maps to multi-maps to vectors to maps and back to vectors. I'm so lost I don't even know what it is you are trying to do. Your posts contradict themselves and I'm not sure we are helping you correctly use any of the containers.
I guess do what you think works and when you run into the problems that many of us have re-iterated several times then come back and ask more questions.
Somehow it feels like we've rattled on for 3 pages and have ended up back where we started.
No, we haven't. We're still at multimaps...
Show me at least two posts which actually contradict themselves, and I might just admit I was wrong (otherwise I wont...). :pQuote:
Your posts contradict themselves and I'm not sure we are helping you correctly use any of the containers.
Not really. The topic of this thread is still what it was originally: swapping the key values and the mapped values of one map around and putting them in a new map (now changed to a multimap, so it can support duplicates).Quote:
I guess do what you think works and when you run into the problems that many of us have re-iterated several times then come back and ask more questions.
Somehow it feels like we've rattled on for 3 pages and have ended up back where we started.
I'd say we have actually advanced far into the night, with the moonlight light enough to be day, since I now have a working swap() function (which I'll probably rename to flipAMap() so it will more descriptive) which can swap any map with two parameters around.
That is more than could be said when I first started this thread... :p
I understand perfectly all of what has been said in this thread, and have applied a lot of the advice to my function. Perhaps it is other people (possible one of them you?) who are not really seeing clearly.
Hahaha. Anyway, I'm off to bed now. Its already 3:12 AM here.
You, if you truly intend to use a map like that. Saying that the current methods "kind of sucked" is bogus when the map is simply not designed to do that. You are using the wrong container for the job, then blaming the container. That sucks, on your part.Quote:
Originally Posted by Programmer_P
Maps and multimaps are not designed for efficient access by sequential index. They are associative containers designed for efficient access by key value. They are not designed for efficient access by mapped value.
For example:Quote:
Originally Posted by Programmer_P
That said, instead of implementing an algorithm, I could have implemented a predicate and used std::find_if, e.g.,Code:template<typename IT, typename T>
bool value_exists(IT begin, IT end, const T& value)
{
for (; begin != end; ++begin)
{
if (begin->second == value)
{
return true;
}
}
return false;
}
#include <map>
#include <string>
int main()
{
std::map<std::string, int> x;
// ...
if (value_exists(x.begin(), x.end(), 123))
{
// ...
}
}
Code:template<typename Pair>
class CompareSecond
{
public:
explicit CompareSecond(const typename Pair::second_type& x) : x_(x) {}
bool operator()(const Pair& y) const
{
return y.second == x_;
}
private:
typename Pair::second_type x_;
};
#include <algorithm>
template<typename IT, typename T>
bool value_exists(IT begin, IT end, const T& value)
{
return std::find_if(begin, end,
CompareSecond<typename IT::value_type>(value)) != end;
}
#include <map>
#include <string>
int main()
{
std::map<std::string, int> x;
// ...
if (value_exists(x.begin(), x.end(), 123))
{
// ...
}
}
No, I'm not. I needed a container which stores two kinds of values, and allows access to one kind of value with the other kind of value, and the map (or multimap) fits that bill.
The bolded statements should not be exclusive to one another. Ideally, a map should allow efficient access by sequential index of the the current key value, i.e. so that you increment the index iterator to access the next key value of the map, and hence, also the mapped value. I was complaining mostly about the syntax necessary to achieve that goal in a map or multimap.Quote:
Maps and multimaps are not designed for efficient access by sequential index. They are associative containers designed for efficient access by key value. They are not designed for efficient access by mapped value.
Ok, but using that method requires writing your own function for using a generic-type of iterator for a map. There is nothing for that already provided by std::map or std::multimap.Quote:
For example:
That said, instead of implementing an algorithm, I could have implemented a predicate and used std::find_if, e.g.,Code:template<typename IT, typename T>
bool value_exists(IT begin, IT end, const T& value)
{
for (; begin != end; ++begin)
{
if (begin->second == value)
{
return true;
}
}
return false;
}
#include <map>
#include <string>
int main()
{
std::map<std::string, int> x;
// ...
if (value_exists(x.begin(), x.end(), 123))
{
// ...
}
}
Code:template<typename Pair>
class CompareSecond
{
public:
explicit CompareSecond(const typename Pair::second_type& x) : x_(x) {}
bool operator()(const Pair& y) const
{
return y.second == x_;
}
private:
typename Pair::second_type x_;
};
#include <algorithm>
template<typename IT, typename T>
bool value_exists(IT begin, IT end, const T& value)
{
return std::find_if(begin, end,
CompareSecond<typename IT::value_type>(value)) != end;
}
#include <map>
#include <string>
int main()
{
std::map<std::string, int> x;
// ...
if (value_exists(x.begin(), x.end(), 123))
{
// ...
}
}
@Programmer_P: I'm just going to re-iterate the point I tried very hard to make earlier, that this should be a class containing a map, not just a map. Simple. Or you can keep digging this hole deeper.
Yes, tie your hands together behind your back while you're at it. It's like trying to give advice to a brick wall. Apparently no one on the entire forum has any sort of knowledge or perspective that's of use to you. Take a step back, try to get out from under your ego for a while and you could actually learn something, instead of feeding this insane fest of miscommunication and denial.
Funny. :D I have actually considered your suggestion seriously, though you probably think I didn't, but decided there is no advantage to putting the swapped map in a class.
And so far, you have not really demonstrated any actual advantage to putting it in a class either that will top my method.
I'm not saying that at all. Yes, I know everyone here has knowledge, more experience, perspective, etc. Otherwise, I wouldn't bother asking any question here at all. But once again, just because you have that, that doesn't make you right all the time either. At least I admit when I'm wrong...Quote:
Yes, tie your hands together behind your back while you're at it. It's like trying to give advice to a brick wall. Apparently no one on the entire forum has any sort of knowledge or perspective that's of use to you. Take a step back, try to get out from under your ego for a while and you could actually learn something, instead of feeding this insane fest of miscommunication and denial.
Every example has but b/c you are so blinded by your own ideas you fail to see them. Every response we get is essentially 'my way is better' and then you ask us to show you a better way. We do show you but you don't see it b/c you refuse to see it.Quote:
And so far, you have not really demonstrated any actual advantage to putting it in a class either that will top my method.
Well in this case you are wrong and you won't admit it. As we have all said time and time again just b/c you can create constructs in the map or with the map or what have you does not mean your design is good. Just b/c it works doesn't mean it's a good design and that you should continue with it. Bad design will code you into a corner and some of those might be impossible to get out of without significant changes. We are telling you that you are using a map for the wrong task. I'm not even sure your program requires said task but you won't listen to that either.Quote:
At least I admit when I'm wrong...
But alas you will not listen and will respond to this with your typical '"I'm right and I know it so show me something different...." and yet we have and you refuse to acknowledge it.
I have not seen anything incorrect from the others that have posted here. They are different approaches that are all fundamentally better than yours. The only posts that I find things that are plainly wrong are all yours.Quote:
I'm not saying that at all. Yes, I know everyone here has knowledge, more experience, perspective, etc. Otherwise, I wouldn't bother asking any question here at all. But once again, just because you have that, that doesn't make you right all the time either.
In theory I agree.
Here's what I think your problem is: you are happy about the things you have done already and don't want to change anything. However, because you have not really written very many serious programs, you are naturally short sighted WRT to design. That's how it goes. Now you are constantly trying to adapt what you have without confronting this fact, which might be hard for you to see, especially if you don't want to. It's called "stubborn beyond belief" ;) People are just being honest and then becoming exasperated at your attitude. Pretty clearly I am not the only one. Like I said, you very much need to get a grip on your ego. It's not any of us who are going to suffer the consequences here, remember. It's up to you to make the most of it, and you really ain't. Seriously.
Ah, but there's something you do not understand about why and how a map is not an array/vector, and that it is not arranged sequentially in memory. That is an inescapable trade-off of data structures (hopefully one day you'll code some yourself). This is a "can't have your cake and eat it too" scenario. The developers of C++, after all, cannot just make up anything they like because it would be nice. They are logically constrained by certain realities, this is one of them. Technically a map is a tree, if you want to look that up.Quote:
Ideally, a map should allow efficient access by sequential index of the the current key value, i.e. so that you increment the index iterator to access the next key value of the map, and hence, also the mapped value. I was complaining mostly about the syntax necessary to achieve that goal in a map or multimap.