C++, How to traverse through a unordered_map - c++

To summarize my problem, in an "unordered_map" I will be adding a pair, name with a number, and sending it to a function. (function doesn't matter what it does.) So then I will be adding another thing to the graph map, but I want to be able to traverse to the next thing I added to the list because I want to throw mark's name into the function next.
Here is some code
unordered_map<string,int> graph;
unordered_map<string,int>:: iterator it;
using namespace std;
int main(){
string name;
graph.insert(pair<string,int>("Sue",4));
it=graph.begin();
name = it -> first; //name is equal to "Sue"
function(name);
graph.insert(pair<string,int>("Mark",83));
it++ // this will not work
name = it -> first; //this will not end up equaling to "Mark"
function(name);
}
Doing ++it does not work,going backwards --it, doesn't work too.
So does anyone have a solution how I can go backwards to obtain the name = it -> first to be Mark?
Note: (I will be doing this method multiple times too because it'll be going into a loop.)

For unordered_map, it's not guaranteed that an item inserted after another item, will also be stored after that item. This is what is meant with an unordered map.
In this case, you are out of luck: the std::hash<std::string> conspires to let "Sue" come after "Mark" and it++ points to graph.end() and it->first is dereferencing this, with a memory error.
If you would use an ordered map<string, int> and insert "Sue" after "Mark" (or you would use std::greater as the map comparison for your original order of "Mark" after "Sue"), then the order of elements is what you expect.
#include <unordered_map>
#include <map>
#include <iostream>
using namespace std;
map<string,int> graph;
map<string,int>:: iterator it;
int main()
{
string name;
graph.insert(pair<string,int>("Mark",4));
it=graph.begin();
name = it -> first; //name is equal to "Sue"
std::cout << name;
graph.insert(pair<string,int>("Sue",83));
it++ ;// this will not work
name = it -> first; //this will not end up equaling to "Mark"
std::cout << name;
}
Live Example.

I think this is because the iterator values are generated when you call Begin().
In some other languages (C#) if a IEnumerable (e.g. List) changes while an IEnumerator (aka. iterator) is active, the Enumerator becomes invalid
and the following IEnumerator::MoveNext() will throw an InvalidOperationException.
see MSDN Article on IEnumerator.MoveNext Method()
Also This article on CPPReference states:
If rehashing occurs due to the insertion, all iterators are invalidated. Otherwise iterators are not affected. References are not invalidated. Rehashing occurs only if the new number of elements is equal to or greater than max_load_factor()*bucket_count().

Related

C++ How to delete nodes in a vector

So I have created a binary search tree that is stored within an array. This binary search tree (BST) stores a user input ID, Age, and Name, then places it within the array sorted in ascending order by the ID.
I'm attempting to write a delete function that takes a user input ID, loops through the vector, and if it finds a matching ID, it deletes it.
However, I can't seem to get BinaryTree.erase() to work due to an error.
Severity Code Description Project File Line Suppression State
Error (active) E0304 no instance of overloaded function "std::vector<_Ty, _Alloc>::erase [with _Ty=Node,
_Alloc=std::allocator]" matches the argument list Project_4CS210
After erasing, I plan on organizing the vector so that there aren't any open spots, however, I want to make sure that I can delete the node first. Any suggestions? I am a beginner, so most of this is rather new to me.
Here's my delete function.
#include "BinaryTree.h"
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int index;
struct Node
{
int ID;
int age;
string name;
Node()
{
}
Node(int id, int Age, string nm)
{
this->ID = id;
this->age = Age;
this->name = nm;
}
};
vector<Node> binaryTree;
void BST::Delete()
{
int input_id;
cout << "What is the ID of the person to be deleted" << endl;
cin >> input_id;
for (unsigned int i = 0; i < binaryTree.size(); i++)
{
if (input_id == binaryTree.at(i).ID)
binaryTree.erase(i);
}
}
std::vector::erase takes an iterator, not an index.
You can use binaryTree.begin() + i to get the iterator for the i-th element of a vector (note that some containers have iterator types without +, so you'd need to use std::advance there).
Overall, binaryTree.erase(binaryTree.begin() + i) should do the job you want, but for what you are trying to do you could also look into std::remove_if.
To expand on this last point (because someone in the comments mentioned the remove/erase idiom):
vector.erase(std::remove_if(vector.begin(),
vector.end(),
[](const auto& element) { return condition(element); }),
vector.end());
This will remove all elements from the vector where condition(element) returns true, and is generally better than iterating over the vector by hand:
If you want to remove more than one element with your method, you might miss some. Take a vector where the elements you want to remove are at indices i and i + 1. Then, you remove the element at index i normally, and the element at i + 1 moves to index i - however, in the next iteration you already check index i + 1, so you miss the item that you wanted to remove
Each time you remove an element from the vector, all the elements behind that are moved around in memory. If your items are fairly big, that can be expensive. Using remove_if should ensure that each element isn't moved more than necessary, which can be more efficient especially if you remove many elements.
You shouldn't remove elements from vector while iterating over it, you can miss some indexes while iterating, use std::remove_if to send elements needed to remove at the end of vector and then erase them from vector with erase() method:
auto It = std::remove_if(binaryTree.begin(), binaryTree.end(), [input](const Node& node)
{
if( node.ID == input )
{
return true;
}
return false;
});
binaryTree.erase(It,binaryTree.end());
Method erase() accepts iterators to remove range or one iterator to remove one element. std::remove_if will return iterator for the first elements pushed at the end to remove.
Considering you wish to remove all the items in the vector or a specific item. You can actually erase items from a vector using erase while iterating. Keep in mind the erase actually provides you with the iterator to the next item. So you may not need to increment the iterator unless it fulfills the condition.
Here is a sample code:
for (std::vector<Node>::iterator it = binaryTree.begin(); it != binaryTree.end();)
{
if(it == yourItem) //Conditional check if it matches with your object.
{
it = binaryTree.erase(it);
}
else
{
++it;
}
}
You can see that i am actually getting the iterator to the next item in the list without incrementing it. You can process it in whatever way you may like.

Comparing unordered_map vs unordered_set

First of all, what is the main difference between them?
The only thing i've found is that unordered_set has no operator [].
How should i access an element in unordered_set, since there is no []?
Which container is using random access to memory(or both)?
And which one of them faster in any sense or using less memory?
They are nearly identical. unordered_set only contains keys, and no values. There is no mapping from a key to a value, so no need for an operator[]. unordered_map maps a key to a value.
You can use the various find methods within unordered_set to locate things.
you can use iterators to access elements.
unordered_set <string> u{
"Dog",
"Cat",
"Rat",
"Parrot",
"bee"
};
for(auto& s:u){
cout << s << ' ';
}
unordered_set<string>::const_iterator point = u.find("bee");
How should I access an element in unordered_set (C++17)?
In C++ 17 a new function extract is added to unordered_set.
Specially, this is the only way to take move only object out of the set.
https://en.cppreference.com/w/cpp/container/unordered_set/extract
For example if you want third element of your unordered set.
Advance the iterator
std::advance(it,2);
Then extarct the value
s.extract(it).value();
Here is the complete code. try on any C++17 compiler.
#include <iostream>
#include <string>
#include <unordered_set>
#include <iterator>
int main()
{
//CREATE AN OBJECT
std::unordered_set<std::string> s;
//INSERT DATA
s.insert("aee");
s.insert("bee");
s.insert("cee");
s.insert("dee");
//NEED TO INCLUDE "iterator" HEADER TO USE "std::advance"
auto it = s.begin();
std::advance(it,2);
//USING EXTRACT
std::string sval = s.extract(it).value();
std::cout<<sval;
}
Note: if queried for out of bound index, nothing happens. No result.
Try changing your code
//ONLY FOUR ELEMENTS
std::advance(it,8);
//USING EXTRACT
std::string sval = s.extract(it).value();

stdext::hash_map unclear hash function

#include <iostream>
#include <hash_map>
using namespace stdext;
using namespace std;
class CompareStdString
{
public:
bool operator ()(const string & str1, const string & str2) const
{
return str1.compare(str2) < 0;
}
};
int main()
{
hash_map<string, int, hash_compare<string, CompareStdString> > Map;
Map.insert(make_pair("one", 1));
Map.insert(make_pair("two", 2));
Map.insert(make_pair("three", 3));
Map.insert(make_pair("four", 4));
Map.insert(make_pair("five", 5));
hash_map<string, int, hash_compare<string, CompareStdString> > :: iterator i;
for (i = Map.begin(); i != Map.end(); ++i)
{
i -> first; // they are ordered as three, five, two, four, one
}
return 0;
}
I want to use hash_map to keep std::string as a key. But when i insert the next pair order is confused. Why order is do not match to insert order ? how should i get the order one two three four five ??
Why order is do not match to insert order?
That's because a stdext::hash_map (and the platform-independent standard library version std::unordered_map from C++11) doesn't maintain/guarantee any reasonable order of its elements, not even insertion order. That's because it is a hashed container, with the individual elements' position based on their hash value and the size of the container. So you won't be able to maintain a reasonable order for your data with such a container.
What you can use to keep your elements in a guaranteed order is a good old std::map. But this also doesn't order elements by insertion order, but by the order induced by the comparison predicate (which can be confugured to respect insertion time, but that would be quite unintuitive and not that easy at all).
For anything else you won't get around rolling your own (or search for other libraries, don't know if boost has something like that). For example add all elements to a linear std::vector/std::list for insertion order iteration and maintain an additional std::(unordered_)map pointing into that vector/list for O(1)/O(log n) retrieval if neccessary.

Why can't I put an iterator in map?

I have a map defined like this
std::map<some_key_type, std::string::iterator> mIteratorMap;
And a huge string named "mHugeString". Then I walk trough the string collecting iterators like this:
std::string::iterator It=mHugeString.begin();
std::string::iterator EndIt=mHugeString.end();
for(;It!=EndIt;++It){
...defining a key element...
if(need_to_store_an_iterator)mIteratorMap[key_of_a_right_type]=It;
}
In the end I should recieve a map, where an iterator is associated with a some sort of key. But the iterator somehow looses itself when being paired with a key by "make_pair", unless it points to a place somewhere in the end of a string. It's hard to tell, but maybe last 256 bytes are fine.
So the question is not how to avoid loosing iterators, it was a stupid idea to store them anyways, but why trying to store an iterator in the begining of the string fails, and why the same with the iterators on the end works fine? What is the difference between them?
I haven't tried it but I would have expected that, of course you can store iterator values as values in a map.
Do you know that if you change the contents of mHugeString then any iterators into it which you have previously stored are now invalid?
You might choose to store the index into the string, instead of the iterator.
I am not sure why it should be problem. I wrote this code to check storage and retrieval of iterator which seems to work fine. [Note: I am not using make_pair as it should not be relevant, give try without using it though!]
#include <string>
#include <iostream>
#include <map>
enum UniqueKey {One, Two, Three};
typedef std::map<UniqueKey, std::string::iterator> UniqueKeyStringMap;
int main()
{
UniqueKeyStringMap storage;
std::string stringOne = "This is one string";
std::string::iterator it = stringOne.begin() + 8; // "o"
std::cout << " Iterator to store: " << std::string(it, it + 3) << std::endl;
storage[UniqueKey::One] = it; // Store iterator
// Retrieve and print, string and map are valid
UniqueKeyStringMap::iterator mapIter = storage.find(UniqueKey::One);
if (mapIter != storage.end())
{
std::cout << " From storage: " <<
std::string(mapIter->second, mapIter->second + 3) << std::endl;
}
}
expected output:
Iterator to store: one
From storage: one
It's fine, you can store iterators in the map. If you get some error, that is caused by something else. Note that if you modify your string, iterators pointing into your string will become invalid.
Please show us a complete, compilable code snippet that is rendered unusable, so we can analyze it.

Now to remove elements that match a predicate?

I have a source container of strings I want to remove any strings from the source container that match a predicate and add them into the destination container.
remove_copy_if and other algorithms can only reorder the elements in the container, and therefore have to be followed up by the erase member function. My book (Josuttis) says that remove_copy_if returns an iterator after the last position in the destination container. Therefore if I only have an iterator into the destination container, how can I call erase on the source container? I have tried using the size of the destination to determine how far back from the end of the source container to erase from, but had no luck. I have only come up with the following code, but it makes two calls (remove_if and remove_copy_if).
Can someone let me know the correct way to do this? I'm sure that two linear calls is not
the way to do this.
#include <iostream>
#include <iterator>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
using namespace std;
class CPred : public unary_function<string, bool>
{
public:
CPred(const string& arString)
:mString(arString)
{
}
bool operator()(const string& arString) const
{
return (arString.find(mString) == std::string::npos);
}
private:
string mString;
};
int main()
{
vector<string> Strings;
vector<string> Container;
Strings.push_back("123");
Strings.push_back("145");
Strings.push_back("ABC");
Strings.push_back("167");
Strings.push_back("DEF");
cout << "Original list" << endl;
copy(Strings.begin(), Strings.end(),ostream_iterator<string>(cout,"\n"));
CPred Pred("1");
remove_copy_if(Strings.begin(), Strings.end(),
back_inserter(Container),
Pred);
Strings.erase(remove_if(Strings.begin(), Strings.end(),
not1(Pred)), Strings.end());
cout << "Elements beginning with 1 removed" << endl;
copy(Strings.begin(), Strings.end(),ostream_iterator<string>(cout,"\n"));
cout << "Elements beginning with 1" << endl;
copy(Container.begin(), Container.end(),ostream_iterator<string>(cout,"\n"));
return 0;
}
With all due respect to Fred's hard work, let me add this: the move_if is no different than remove_copy_if at an abstract level. The only implementation level change is the end() iterator. You are still not getting any erase(). The accepted answer does not erase() the matched elements -- part of the OP's problem statement.
As for the OP's question: what you want is an in-place splice. This is possible for lists. However, with vectors this will not work. Read about when and how and why iterators are invalidated. You will have to take a two pass algorithm.
remove_copy_if and other algorithms can only reorder the elements in the container,
From SGI's documentation on remove_copy_if:
This operation is stable, meaning that the relative order of the elements that are copied is the same as in the range [first, last).
So no relative reordering takes place. Moreover, this is a copy, which means the elements from Source vector in your case, is being copied to the Container vector.
how can I call erase on the source container?
You need to use a different algorithm, called remove_if:
remove_if removes from the range [first, last) every element x such that pred(x) is true. That is, remove_if returns an iterator new_last such that the range [first, new_last) contains no elements for which pred is true. The iterators in the range [new_last, last) are all still dereferenceable, but the elements that they point to are unspecified. Remove_if is stable, meaning that the relative order of elements that are not removed is unchanged.
So, just change that remove_copy_if call to:
vector<string>::iterator new_last = remove_if(Strings.begin(),
Strings.end(),
Pred);
and you're all set. Just keep in mind, your Strings vector's range is no longer that defined by the iterators [first(), end()) but rather by [first(), new_last).
You can, if you want to, remove the remaining [new_last, end()) by the following:
Strings.erase(new_last, Strings.end());
Now, your vector has been shortened and your end() and new_last are the same (one past the last element), so you can use as always:
copy(Strings.begin(), Strings.end(), ostream_iterator(cout, "\"));
to get a print of the strings on your console (stdout).
I see your point, that you'd like to avoid doing two passes over your source container. Unfortunately, I don't believe there's a standard algorithm that will do this. It would be possible to create your own algorithm that would copy elements to a new container and remove from the source container (in the same sense as remove_if; you'd have to do an erase afterward) in one pass. Your container size and performance requirements would dictate whether the effort of creating such an algorithm would be better than making two passes.
Edit: I came up with a quick implementation:
template<typename F_ITER, typename O_ITER, typename FTOR>
F_ITER move_if(F_ITER begin, F_ITER end, O_ITER dest, FTOR match)
{
F_ITER result = begin;
for(; begin != end; ++begin)
{
if (match(*begin))
{
*dest++ = *begin;
}
else
{
*result++ = *begin;
}
}
return result;
}
Edit:
Maybe there is confusion in what is meant by a "pass". In the OP's solution, there is a call to remove_copy_if() and a call to remove_if(). Each of these will traverse the entirety of the original container. Then there is a call to erase(). This will traverse any elements that were removed from the original container.
If my algorithm is used to copy the removed elements to a new container (using begin() the original container for the output iterator will not work, as dirkgently demonstrated), it will perform one pass, copying the removed elements to the new container by means of a back_inserter or some such mechanism. An erase will still be required, just as with remove_if(). One pass over the original container is eliminated, which I believe is what the OP was after.
There will be copy_if and remove_if.
copy_if( Strings.begin(), Strings.end(),
back_inserter(Container), not1(Pred) );
Strings.erase( remove_if( Strings.begin(), Strings.end(), not1(Pred) ),
Strings.end() );
It is better to understand code where Predicate class answering "true" if something is present. In that case you won't need not1 two times.
Because std::find looks for substring not obligatory from the begin you need to change "beginning with 1" to "with 1" to avoid future misunderstanding of your code.
The whole reason why the remove_* algorithms do not erase elements is because it is impossible to "erase" an element by the iterator alone. You can't get container by iterator
This point is explained in more details in the book "Effective STL"
Use 'copy_if', followed by 'remove_if'. remove_copy_if does not modify the source.
On lists you can do better - reordering followed by splice.
If you don't mind having your strings in the same container, and having just an iterator to separate them, this code works.
#include "stdafx.h"
#include <iostream>
#include <iterator>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
using namespace std;
class CPred : public unary_function<string, bool>
{
public:
CPred(const string& arString)
:mString(arString)
{
}
bool operator()(const string& arString) const
{
return (arString.find(mString) == std::string::npos);
}
private:
string mString;
};
int main()
{
vector<string> Strings;
Strings.push_back("213");
Strings.push_back("145");
Strings.push_back("ABC");
Strings.push_back("167");
Strings.push_back("DEF");
cout << "Original list" << endl;
copy(Strings.begin(), Strings.end(),ostream_iterator<string>(cout,"\n"));
CPred Pred("1");
vector<string>::iterator end1 =
partition(Strings.begin(), Strings.end(), Pred);
cout << "Elements matching with 1" << endl;
copy(end1, Strings.end(), ostream_iterator<string>(cout,"\n"));
cout << "Elements not matching with 1" << endl;
copy(Strings.begin(), end1, ostream_iterator<string>(cout,"\n"));
return 0;
}
remove*() don't relally remove elements, it simply reorders them and put them at the end of the collection and return a new_end iterator in the same container indicating where the new end is. You then need to call erase to remove the range from the vector.
source.erase(source.remove(source.begin(), source.end(), element), source.end());
remove_if() does the same but with a predicate.
source.erase(source.remove_if(source.begin(), source.end(), predicate), source.end());
remove_copy_if() will only copy the elements NOT matching the predicate, leaving the source vector intact and providing you with the end iterator on the target vector, so that you can shrink it.
// target must be of a size ready to accomodate the copy
target.erase(source.remove_copy_if(source.begin(), source.end(), target.begin(), predicate), target.end());