Get index of element in C++ map - c++

I have a std::map called myMap in my C++ application, and I want to get an element using either myMap.find(key) or myMap[key]. However, I would also like to get the index of that element in the map.
std::map<string, int> myMap;
// Populate myMap with a bunch of items...
myElement = myMap["myKey"];
// Now I need to get the index of myElement in myMap
Is there a clean way to do that?
Thank you.

I came here seeking for this answer but i found this
distance function takes 2 iterators and returns an index
cout << distance(mymap.begin(),mymap.find("198765432"));
hope this helps :D

A std::map doesn't really have an index, instead it has an iterator for a key / value pair. This is similar to an index in that it represents a position of sorts in the collection but it is not numeric. To get the iterator of a key / value pair use the find method
std::map<string, int>::iterator it = myMap.find("myKey");

Most of the time when you are working with indices and maps, it usually means that your map is fixed after some insertions. If this assumption holds true for your use case, you can use my answer.
If your map is already fixed (you wouldn't add/delete any key afterward), and you want to find an index of a key, just create a new map that maps from key to index.
std::map<string, int> key2index; // you can use unordered_map for it to be faster
int i = 0;
for (pair<K, V> entry : yourMap) {
key2index[entry.first] = i++;
}
From this key2index map you can query the key as often as you can. Just call key2index['YourKey'] to get your index.
The benefit of this method over distance function is access time complexity. It's O(1) and very fast if you do query often.
Extra Section
If you want to do the opposite, you want to access key from index then do the following.
Create an array or vector that stores keys of your entire map. Then you can access the key by specifying the index.
vector<int> keys;
for (pair<K,V> entry : yourMap) {
keys.push_back(entry.first);
}
To access an index i of your map, use yourMap[keys[i]]. This is also O(1) and significantly faster because it's using only an array/vector, not a map.

Well - map is keeping the key and the data as a pair
so you can extract key by dereferecing the map's iterator into pair or directly into pair's first element.
std::map<string, int> myMap;
std::map<string, int>::iterator it;
for(it=myMap.begin();it!=myMap.end();it++)
{
std::cout<<it->first<<std::endl;
}

Use
int k = distance(mymap.begin(), mymap.find(mykey));
It will give you the index of the key element.

There is no such thing as an index in a map. Maps are not stored (not necessarly, at least; and indeed they are not in most implementations) as a sequence of "pairs".
Regardless of the implementation, however, std::map does not model a container having an index.
Depending on what you are asking this question for, the "index" can be an iterator (as suggested by others) or the key itself.
However, it sounds strange you asked this question. If you could give us a bit more details we would probably be able to point you to a better solution to your problem.

The semantic of a map does not include indexes. To understand that, you can note that Maps are typically implemented as trees. Therefore, elements in it do not have an index (try to define an index in a natural way for a tree).

Map is a key-value data structure which internally data in a tree structure. There are O(n) solution stated above.
" distance(mymap.begin(),mymap.find("198765432")) " will not bring you the correct answer.
For your requirement, you have to build your own segment tree type data structure for O log(n) competitive operations.

A use case: if you want to know how many items are smaller or equal as you progress on a vector. Constraint : i < = j, how many v[i]'s are smaller or equal to v[j]). let's insert it into a map or set.
vector<int> v={1, 4, 2, 3};
set<int> s;
s = {1}; // 1's position is 1 (one based)
s = {1,4}; //4's positon is 2
s = {1, 2, 4} ;//2's position is 2
s = {1 , 2, 3, 4}; //3's positon is 3
it seems std:distance would need a O(n) time.
I could achieve same affect using set.lower_bound() and counting backward till set.begin(). Does anyone have a better solution than requiring O(n) , perhaps using additional data structures?
OK, on a second thought here is a solution to store index (1 based) for this specific problem. However it may not solve the problem for get the correct index of items in the finished map.
vector<int> arr={1 , 1 , 2, 4, 2};
multimap<int, int> track;
for(auto a:arr)
{
auto it = track.insert(make_pair(a, 1)); //first item is 1
if(it!=track.begin())
{
--it;
int prev=it->second;
it++;
it->second+=prev;
}
cout<<a<<','<<it->second-1<<endl;
}

Related

Stable sort a C++ hash map - preserve the insertion order for equal elements

Say I have a std::unordered_map<std::string, int> that represents a word and the number of times that word appeared in a book, and I want to be able to sort it by the value.
The problem is, I want the sorting to be stable, so that in case two items have equal value I want the one who got inserted first to the map to be first.
It is simple to implement it by adding addition field that will keep the time it got inserted. Then, create a comperator that uses both time and the value. Using simple std::sort will give me O(Nlog(N)) time complexity.
In my case, space is not an issue whenever time can be improved. I want to take advantage of it and do a bucket sorting. Which should give me O(N) time complexity. But when using bucket sorting, there is no comperator, when iterating the items in the map the order is not preserved.
How can I both make it stable and still keep the O(N) time complexity via bucket sorting or something else?
I guess that if I had some kind of hash map that preserves the order of insertion while iterating it, it would solve my issue.
Any other solutions with the same time complexity are acceptable.
Note - I already saw this and that and due to the fact that they are both from 2009 and that my case is more specific I think, I opened this question.
Here is a possible solution I came up with using an std::unordered_map and tracking the order of inserting using a std::vector.
Create a hash map with the string as key and count as value.
In addition, create a vector with iterators to that map type.
When counting elements, if the object is not yet in the map, add to both map and vector. Else, just increment the counter. The vector will preserve the order the elements got inserted to the map, and the insertion / update will still be in O(1) time complexity.
Apply bucket sort by iterating over the vector (instead of the map), this ensures the order is preserved and we'll get a stable sort. O(N)
Extract from the buckets to make a sorted array. O(N)
Implementation:
unordered_map<std::string, int> map;
std::vector<std::unordered_map<std::string,int>::iterator> order;
// Lets assume this is my string stream
std::vector<std::string> words = {"a","b","a" ... };
// Insert elements to map and the corresponding iterator to order
for (auto& word : words){
auto it = map.emplace(word,1);
if (!it.second){
it.first->second++;
}
else {
order.push_back(it.first);
}
max_count = std::max(max_count,it.first->second);
}
// Bucket Sorting
/* We are iterating over the vector and not the map
this ensures we are iterating by the order they got inserted */
std::vector<std::vector<std::string>> buckets(max_count);
for (auto o : order){
int count = o->second;
buckets[count-1].push_back(o->first);
}
std::vector<std::string> res;
for (auto it = buckets.rbegin(); it != buckets.rend(); ++it)
for (auto& str : *it)
res.push_back(str);

C++ - Modifying key for all map elements

Let's consider this code:
std::map< int, char > charMap;
for( auto& i : charMap )
{
charMap[ i.first + 1 ] = charMap[ i.first ];
charMap.erase( i.first );
}
Let's say that the map has some values with randomed keys. I am trying to shift the keys by 1.
This won't work because the loop goes on forever.
Is there a fast way to make it work?
In C++17, you can use node extraction and splicing (see P0083R3):
std::map<int, char> tmpMap;
for (auto it = charMap.begin(); it != charMap.end(); )
{
auto nh = charMap.extract(it++); // node handle
++nh.key();
tmpMap.insert(tmpMap.end(), std::move(nh));
}
tmpMap.swap(charMap);
The loop extracts consecutive map nodes, mutates them, and reinserts the node into tmpMap (now with the different key). At the end, charMap is empty and tmpMap contains all the elements with their modified keys, so we swap the two.
Before C++17, you would have to copy (or move) the value data around to insert a new element with a new key.
std::map<int, char> tmpMap;
for (auto & p : charMap)
tmpMap.emplace_hint(tmpMap.end(), p.first + 1, std::move(p.second));
tmpMap.swap(charMap);
This requires memory allocations for the nodes, though, so the new splicing-based solution is more efficient.
In either case we can use the hinted insertion, because we are reconstructing elements in the same order and so the newest element is always inserted at the end.
Ad hoc solution using the known impact on order
You could simply opt for a backward iteration, starting from the last element:
for( auto pi = charMap.end(); pi-- != charMap.begin(); pi=charMap.erase( pi ))
charMap[ pi->first + 1 ] = charMap[ pi->first ];
Online demo
This will not loop forever here, because the new element that you insert will always be after the current one and will hence not be reprocessed again and again.
More general solution
For a more general transformation where you can't be sure about the impact on element ordering, I'd rather go for a std::transform():
std::map<int, char> tmp;
std::transform(charMap.begin(), charMap.end(), std::inserter(tmp,tmp.begin()),
[](auto e) { return std::make_pair(e.first+1, e.second); });
std::swap(tmp, charMap); // the old map will be discarded when tmp goes out of scope
Online demo
You cannot use this kind of range iteration for two fundamental reasons:
The first reason is that a fundamental property of a map is that iterating over the map iterates in key order.
You are iterating over the map. So, if the first key in the map is key 0, you will copy the value to key 1. Then, you iterate to the next key in the map, which is the key 1 that you just created, and then copy it to key 2. Lather, rinse, repeat.
The are several ways to solve this, but none of that matters because of a second fundamental aspect of the map:
charMap[1]=charMap[0];
This copes charMap[0] to charMap[1]. It does nothing to charMap[0]. It is still there. Nothing happened to it. So, presuming that the lowest key in the map is 0, and you shifted the keys correctly, you will still have a value in the map with key 0. Ditto for the everything else in the map.
But let's say you solved the first problem in one of the several ways that it could be solved. Then, let's say your map has values for keys 0, 5, and 7.
After you copy key #0 to key #1, key #5 to key #6, and key #7 to key #8, take a paper and pencil, and figure out what you have now in your map.
Answer: it is not going to be keys 1, 6, and 8. It will be keys 0, 1, 5, 6, 7, and 8.
All you did was copy each value to the next key. This is because a computer does exactly what you tell it to do, no more, no less. A computer does not do what you want it to do.
The easiest way to do this is to create a new map, and copy the contents of the old map to the new map, with an updated key value. You can still use range iteration for that. Then, replace the old map with the new map.
Of course, this becomes impractical if the map is very large. In that case, it is still possible to do this without using a second map, but the algorithm is going to be somewhat complicated. The capsule summary is:
1) Iterate over the keys in reverse order. Can't use range iteration here.
2) After copying the key to the next value in the map, explicitly remove the value from its original key.

In a C++ map, is there any way to search for the key given a value?

In a C++ std::map, is there any way to search for the key given the mapped value? Example:
I have this map:
map<int,string> myMap;
myMap[0] = "foo";
Is there any way that I can find the corresponding int, given the value "foo"?
cout << myMap.some_function("foo") <<endl;
Output: 0
std::map doesn't provide a (fast) way to find the key of a given value.
What you want is often called a "bijective map", or short "bimap". Boost has such a data structure. This is typically implemented by using two index trees "glued" together (where std::map has only one for the keys). Boost also provides the more general multi index with similar use cases.
If you don't want to use Boost, if storage is not a big problem, and if you can affort the extra code effort, you can simply use two maps and glue them together manually:
std::map<int, string> myMapForward;
std::map<string, int> myMapBackward; // maybe even std::set
// insertion becomes:
myMapForward.insert(std::make_pair(0, "foo"));
myMapBackward.insert(std::make_pair("foo", 0));
// forward lookup becomes:
myMapForwar[0];
// backward lookup becomes:
myMapBackward["foo"];
Of course you can wrap those two maps in a class and provide some useful interface, but this might be a bit overkill, and using two maps with the same content is not an optional solution anyways. As commented below, exception safety is also a problem of this solution. But in many applications it's already enough to simply add another reverse map.
Please note that since std::map stores unique keys, this approach will support backward lookup only for unique values, as collisions in the value space of the forward map correspond to collisions in the key space of the backward map.
No, not directly.
One option is to examine each value in the map until you find what you are looking for. This, obviously, will be O(n).
In order to do this you could just write a for() loop, or you could use std::find_if(). In order to use find_if(), you'll need to create a predicate. In C++11, this might be a lambda:
typedef std::map <unsigned, Student> MyMap;
MyMap myMap;
// ...
const string targetName = "Jones";
find_if (myMap.begin(), myMap.end(), [&targetName] (const MyMap::value_type& test)
{
if (test.second.mName == targetName)
return true;
});
If you're using C++03, then this could be a functor:
struct MatchName
: public std::unary_function <bool, MyMap::value_type>
{
MatchName (const std::string& target) : mTarget (target) {}
bool operator() (const MyMap::value_type& test) const
{
if (test.second.mName == mTarget)
return true;
return false;
}
private:
const std::string mTarget;
};
// ...
find_if (myMap.begin(), myMap.end(), MatchName (target));
Another option is to build an index. The index would likely be another map, where the key is whatever values you want to find and the value is some kind of index back to the main map.
Suppose your main map contains Student objects which consist of a name and some other stuff, and the key in this map is the Student ID, an integer. If you want to find the student with a particular last name, you could build an indexing map where the key is a last name (probably want to use multimap here), and the value is the student ID. You can then index back in to the main map to get the remainder of the Student's attributes.
There are challenges with the second approach. You must keep the main map and the index (or indicies) synchronized when you add or remove elements. You must make sure the index you choose as the value in the index is not something that may change, like a pointer. If you are multithreading, then you have to give a think to how both the map and index will be protected without introducing deadlocks or race conditions.
The only way to accomplish this that I can think of is to iterate through it. This is most likely not what you want, but it's the best shot I can think of. Good luck!
No, You can not do this. You simply have to iterate over map and match each value with the item to be matched and return the corresponding key and it will cost you high time complexity equal to O(n).
You can achieve this by iterating which will take O(n) time. Or you can store the reverse map which will take O(n) space.
By iterating:
std::map<int, string> fmap;
for (std::map<int,string>::iterator it=fmap.begin(); it!=fmap.end(); ++it)
if (strcmp(it->second,"foo"))
break;
By storing reverse map:
std::map<int, string> fmap;
std::map<string, int> bmap;
fmap.insert(std::make_pair(0, "foo"));
bmap.insert(std::make_pair("foo", 0));
fmap[0]; // original map lookup
bmap["foo"]; //reverse map lookup

Accessing map value by index

If I have a structure like
std::map<string, int> myMap;
myMap["banana"] = 1;
myMap["apple"] = 1;
myMap["orange"] = 1;
How can I access myMap[0]?
I know that the map sorts internally and I'm fine with this, I want to get a value in the map by index. I've tried myMap[0] but I get the error:
Error 1 error C2679: binary '[' : no operator found which takes a right-hand operand of type 'int' (or there is no acceptable conversion)
I realise I could do something like this:
string getKeyAtIndex (int index){
map<string, int>::const_iterator end = myMap.end();
int counter = 0;
for (map<string, int>::const_iterator it = myMap.begin(); it != end; ++it) {
counter++;
if (counter == index)
return it->first;
}
}
But surely this is hugely inefficient? Is there a better way?
Your map is not supposed to be accessed that way, it's indexed by keys not by positions. A map iterator is bidirectional, just like a list, so the function you are using is no more inefficient than accessing a list by position. If you want random access by position then use a vector or a deque.
Your function could be written with help from std::advance(iter, index) starting from begin():
auto it = myMap.begin();
std::advance(it, index);
return it->first;
There may be an implementation specific (non-portable) method to achieve your goal, but not one that is portable.
In general, the std::map is implemented as a type of binary tree, usually sorted by key. The definition of the first element differs depending on the ordering. Also, in your definition, is element[0] the node at the top of the tree or the left-most leaf node?
Many binary trees are implemented as linked lists. Most linked lists cannot be directly accessed like an array, because to find element 5, you have to follow the links. This is by definition.
You can resolve your issue by using both a std::vector and a std::map:
Allocate the object from dynamic memory.
Store the pointer, along with the key, into the std::map.
Store the pointer in the std::vector at the position you want it
at.
The std::map will allow an efficient method to access the object by key.
The std::vector will allow an efficient method to access the object by index.
Storing pointers allows for only one instance of the object instead of having to maintain multiple copies.
Well, actually you can't. The way you found is very unefficient, it have a computational complexity of O(n) (n operations worst case, where n is the number of elements in a map).
Accessing an item in a vector or in an array have complexity O(1) by comparison (constant computational complexity, a single operation).
Consider that map is internally implemented as a red black tree (or avl tree, it depends on the implementation) and every insert, delete and lookup operation are O(log n) worst case (it requires logarithm in base 2 operations to find an element in the tree), that is quite good.
A way you can deal with is to use a custom class that have inside both a vector and a map.
Insertion at the end of the class will be averaged O(1), lookup by name will be O(log n), lookup by index will be O(1) but in this case, removal operation will be O(n).
Previous answer (see comment): How about just myMap.begin();
You could implement a random-access map by using a vector backing-store, which is essentially a vector of pairs. You of course lose all the benefits of the standard library map at that point.
you can use some other map like containers .
keep a size fields can make binary search tree easy to random access .
here is my implementation ...
std style , random access iterator ...
size balanced tree ...
https://github.com/mm304321141/zzz_lib/blob/master/sbtree.h
and B+tree ...
https://github.com/mm304321141/zzz_lib/blob/master/bpptree.h
std::map is an ordered container, but it's iterators don't support random access, but rather bidirectional access. Therefore, you can only access the nth element by navigating all its prior elements. A shorter alternative to your example is using the standard iterator library:
std::pair<const std::string, int> &nth_element = *std::next(myMap.begin(), N);
This has linear complexity, which is not ideal if you plan to frequently access this way in large maps.
An alternative is to use an ordered container that supports random access. For example, boost::container::flat_map provides a member function nth which allows you exactly what you are looking for.
std::map<string,int>::iterator it = mymap.begin() + index;

Simple and effective way to store data that can be accessed though key or ordinal c++

I need to create a data structure that can access elements by a string key, or by their ordinal.
the class currently uses an array of nodes that contain the string key and a pointer to whatever element. This allows for O(n) looping through, or O(1) getting an element by ordinal, however the only way I've found to find an element by key is doing an O(n) loop and comparing keys until I find what I want, which is SLOW when there are 1000+ elements. is there a way to use the key to reference the pointer, or am I out of luck?
EDIT: the by ordinal is not so much important as the O(n) looping. This is going to be used as a base structure that will be inherited for use in other ways, for instance, if it was a structure of draw able objects, i'd want to be able to draw all of them in a single loop
You can use std::map for O(log n) searching speed. View this branch for more details. In this branch exactly your situation (fast retrieving values by string or/and ordinal key) is discussed.
Small example (ordinal keys are used, you can do similiar things with strings):
#include <map>
#include <string>
using std::map;
using std::string;
struct dummy {
unsigned ordinal_key;
string dummy_body;
};
int main()
{
map<unsigned, dummy> lookup_map;
dummy d1;
d1.ordinal_key = 10;
lookup_map[d1.ordinal_key] = d1;
// ...
unsigned some_key = 20;
//determing if element with desired key is presented in map
if (lookup_map.find(some_key) != lookup_map.end())
//do stuff
}
If you seldom modify your array you can just keep it sorted and use binary_search on it to find the element by key in O(logn) time (technically O(klogn) since you're using strings [where k is the average length of a key string]).
Of course this (just like using a map or unordered_map) will mess up your ordinal retrieval since the elements are going to be stored in sorted order not insertion order.
Use vector and map:
std::vector<your_struct> elements;
std::map<std::string, int> index;
Map allows you to retrieve the key's index in O(lg n) time, whereas the vector allows O(1) element access by index.
Use a hashmap