As far as I looked through the C++ reference, there is no function which returns a vector (or similar) containing all the keywords of a map. Why is this so? This is not a super important function, but sometimes it can be useful. (I don't exactly understand the key_comp function, but this seems to be not what I'm looking for)
Just for completeness, here is some function which does what I want:
template<typename key_t, typename value_t>
std::vector<key_t> getKeywords(const std::map<key_t, value_t>& m){
std::vector<key_t> keywords;
for(const auto& it: m){
keywords.push_back(it.first);
}
return keywords;
}
This is for the same reason that there is no operator+ for std::list iterators.
The standard tries not to encourage inefficient operations, in preference for you choosing the appropriate container for the job.
Creating a vector of all map keys is non-trivial because such a vector is not maintained internally along with the map.
You can of course create it if you really want, as you've already shown. But the standard wants you to know that you probably should be looking for another approach.
For example, if you just want to iterate over the keys, you can iterate over the whole map instead and use only the first part of each resultant pair.
Also, the standard library is intended to give you building blocks, not to suit every possible use case imaginable.
The two chief reasons are that most implementations do not have a vector of keywords intenally, so they would have to create it on the fly. And as you can see, creating it isn't really hard so there is little need to have it in the Standard,
Indeed, the keys are not accessible as such because the concern of the map class is O(log(N)) retrieval, not iteration.
As a general guideline when using the stl, choose your containers according to the use case of your application. If you want fast iteration, you can use a vector of tuple<Key, Value> and sort it, or use a pair<vector<Key>, vector<Value>> that you keep sorted by hand. std::make_heap and the like can be handy for this approach.
Retrieving the map keys can be done like your implementation.
Related
I am using a header-only json library and it uses a std::map. I would prefer although to have it not be ordered.
https://github.com/nlohmann/json/blob/develop/src/json.hpp#L371
There is the snippet that I'm wondering if I can fix. Assuming "ObjectType" is a std::map. Is there any way to remove the order from it or somehow make the std::less<StringType> irrelevant.
It seems that changing the source to support std::unordered_map would be too large of a task to be worth it.
First of all, std::unordered_map is not a viable solution here, since it will not preserve the insertion order either. "Unordered" here means pretty much ignorant to any ordering whatsoever.
Instead for your particular task you want to somehow save the original insertion order, so here are some options:
change std::map key to the index number or replace std::map with std::vector. The latter actually makes sense even if you want to retain the ability to search by the object name, as JSON objects don't tend to get too big so linear search probably won't introduce any noticeable drawback.
find a way to store the desired ordering separately. std::vector of keys can handle the storage, and you can add some iterator trickery to make your container cycle over the preferred order, e.g. by overloading begin() and end() methods.
use a multiple-keyed map as a ready solution - boost::multiindex is a default choice.
First off, I would like to make a few points I believe to be true. Please can these be verified?
A hash map stores strings by
converting them into an integer
somehow.
std::map is not a hash map, and if I'm using strings, I should consider using a hash map for memory issues?
String compares are not good to rely on.
If std::map is not a hash map and I should not be relying on string compares (basically, I have a map with strings as keys...I was told to look up using hash maps instead?), is there a hash map in the C++ STL? If not, how about Boost?
Secondly, Is a hash map worth it for [originally] an std::map< std::string, non-POD GameState >?
I think my point is getting across...I plan to have a store of different game states I can look up and register into a factory.
If any more info is needed, please ask.
Thanks for your time.
I don't believe most of your points are correct.
there is no hash map in the current standard. C++0x introduces unordered_map, who's implementation will be a hash table and your compiler probably already supports it.
std::map is implemented as a balanced tree, not a hash table. There are no "memory issues" when using either map type with strings, either as keys or data.
strings are not stored as numbers in either case - an unordered_map will use a hashing function to derive a numeric key from the string, but this is not stored.
my experience is that unordered_map is about twice the speed of map - they have basically the same interface, so you can try both with your own data - whenever you are interested in performance you should always perform tests your self with your own real data, rather than depending on the experience of others. Both map types will be somewhat sensitive to the length of the string key.
Assuming you have some class A, that you want to access via a string key, the maps would be declared as:
map <string, A> amap;
unordered_map <string, A> umap;
I made a benchmark that compares std::map with boost::unordered_map.
My conclusion was basically this: If you do not need map-specific things like equal_range, then always use boost::unordered_map.
The full benchmark can be found here
A hash map will typically have some integral representation of a string, yes.
std::map has a requirement to be sorted, so implementing it as a hash table is unlikely, and I've never seen it in practice.
Whether string comparisons are good or bad depends entirely on what you're doing, what data you're comparing, and how often. If the first letter differs then that's barely any different from an integer comparison, for example.
You want unordered_map (that's the Boost version - there is also a version in the TR1 standard library if your compiler has that).
Is it worth it for game states? Yes, but only because using an unordered_map is simple. You're prematurely worrying about optimisations at this stage. Save the worries over access patterns for things you're going to look up thousands of times a second (ie. when your profiler tells you that it's a problem).
I am a C++ newbie trying to use a map so I can get constant time lookups for the find() method.
The problem is that when I use an iterator to go over the elements in the map, elements do not appear in the same order that they were placed in the map.
Without maintaining another data structure, is there a way to achieve in order iteration while still retaining the constant time lookup ability?
Please let me know.
Thanks,
jbu
edit: thanks for letting me know map::find() isn't constant time.
Without maintaining another data structure, is there a way to achieve in order iteration while still retaining the constant time lookup ability?
No, that is not possible. In order to get an efficient lookup, the container will need to order the contents in a way that makes the efficient lookup possible. For std::map, that will be some type of sorted order; for std::unordered_map, that will be an order based on the hash of the key.
In either case, the order will be different then the order in which they were added.
First of all, std::map guarantees O(log n) lookup time. You might be thinking about std::tr1::unordered_map. But that by definitions sacrifices any ordering to get the constant-time lookup.
You'd have to spend some time on it, but I think you can bash boost::multi_index_container to do what you want.
What about using a vector for the keys in the original order and a map for the fast access to the data?
Something like this:
vector<string> keys;
map<string, Data*> values;
// obtaining values
...
keys.push_back("key-01");
values["key-01"] = new Data(...);
keys.push_back("key-02");
values["key-02"] = new Data(...);
...
// iterating over values in original order
vector<string>::const_iterator it;
for (it = keys.begin(); it != keys.end(); it++) {
Data* value = values[*it];
}
I'm going to actually... go backward.
If you want to preserve the order in which elements were inserted, or in general to control the order, you need a sequence that you will control:
std::vector (yes there are others, but by default use this one)
You can use the std::find algorithm (from <algorithm>) to search for a particular value in the vector: std::find(vec.begin(), vec.end(), value);.
Oh yes, it has linear complexity O(N), but for small collections it should not matter.
Otherwise, you can start looking up at Boost.MultiIndex as already suggested, but for a beginner you'll probably struggle a bit.
So, shirk the complexity issue for the moment, and come up with something that work. You'll worry about speed when you are more familiar with the language.
Items are ordered by operator< (by default) when applied to the key.
PS. std::map does not gurantee constant time look up.
It gurantees max complexity of O(ln(n))
First off, std::map isn't constant-time lookup. It's O(log n). Just thought I should set that straight.
Anyway, you have to specify your own comparison function if you want to use a different ordering. There isn't a built-in comparison function that can order by insertion time, but, if your object holds a timestamp field, you can arrange to set the timestamp at the time of insertion, and using a by-timestamp comparison.
Map is not meant for placing elements in some order - use vector for that.
If you want to find something in map you should "search" by the key using [the operator
If you need both: iteration and search by key see this topic
Yes you can create such a data structure, but not using the standard library... the reason is that standard containers can be nested but cannot be mixed.
There is no problem implementing for example a map data structure where all the nodes are also in a doubly linked list in order of insertion, or for example a map where all nodes are in an array. It seems to me that one of these structures could be what you're looking for (depending which operation you prefer to be fast), but neither of them is trivial to build using standard containers because every standard container (vector, list, set, ...) wants to be the one and only way to access contained elements.
For example I found useful in many cases to have nodes that were at the same time in multiple doubly-linked lists, but you cannot do that using std::list.
Is this possible?
#include <map>
class Example {
private:
std::map<std::string, std::string, less<std::string>,
std::allocator< CustomPair<std::string, std::string> > > myMap;
};
In the example above, CustomPair would be a template class holding a key and value. If this is possible, is it that simple or is there anything I should look out for?
One can only speculate what your real intent is here, so I assume you already have a class that contains both key and value. In that case std::set with a custom comparison may be a better choice than a std::map.
You then need to provide a comparison that will only compare the key part of your class and the key part must be const (not change over time) as long as the object is in the set.
As mentioned in the comment the elements of a set are only accessable as consts, so if you want to change the value of a such element you need to const_cast the write access or declare the member mutable.
In another answer iain made another very good suggestion. If you rarely insert into the container and mostly access the container searching for elements then a sorted std::vector and std::binary_search are a very effective alternative to the set.
I would be more likely to use std::set.
I would either use a set as described by lothar or use an sorted std::vector as described in "Effective STL" chapter 23: "Consider replacing associative containers with sorted vectors".
The rational for this is that a std::binary_search of a sorted vector with a custom comparitor is nearly as fast and sometimes faster than a map lookup and iteration is much faster. The insert operations are more expensive though (you have to call sort after each insert). A lot of map use cases insert very infrequently though.
The vector would be more flexibility than the set.
I replaced a map of 2000 complex objects (indexed by int) with this approach, iteration and processing every object in the map went from 50 seconds to less than 5 on a server class system. There was no noticeable difference for map lookups times.
I think you can do it but will not get the desired effect, because the use of std::allocator will be done via rebind<std::pair>, thus overriding your selection of CustomPair. In fact, it probably doesn't matter what type you put there, the STL functions will ignore it. At least some of them will definitely do this, but I'm not sure all will. Strictly speaking this is almost certainly implementation dependent. I don't know what the standard says.
I am looking for a container which provides std::map like interface but maintains the order in which elements are inserted. Since there will not be too many elements in the map, the lookup performance is not a big issue. Will boost::unordered_map work in this case? i.e. does it maintain the order of insertion. I am new to boost library and hence want to know what exactly meant by 'unordered' ?
Read about Boost.Multiindex. It gives you an opportunity to create a container that has both access to data by key (like std::map) and sequenced acess to data (like std::list).
This is an example.
unordered_map doesn't maintain the order of insertion. Unordered in this case means that the observable order of elements (i.e. when you enumerate them) is unspecified and arbitrary. In fact, I would expect that the order of elements in an unordered_map can change during the lifetime of the map, due to rehashing when resizing the map (that's implementation dependent though)
When I needed this last time, I used a std::vector< std::pair<const Key, Value> >. I didn't need much of the std::map interface so I didn't bother with is, but it seems it should be fairly easy to slap a map-like interface around this.
Also, be sure to look at the answers to this question.