Replacing elements in vector using erase and insert - c++

void replace(vector<string> my_vector_2, string old, string replacement){
vector<string>::iterator it;
for (it = my_vector_2.begin(); it != my_vector_2.end(); ++it){
if (*it==old){
my_vector_2.erase(it);
my_vector_2.insert(it,replacement);
}
}
}
So, I'd like this function to replace all occurrences of the string old in the vector with the string replacement. But when calling this function, it simply doesn't change the vector at all. I'm not sure if I am using the erase and insert functions properly. Any ideas?

At first you need to pass vector by reference, not by value.
void replace(vector<string>& my_vector_2, string old, string replacement){
Second erase and insert invalidates it, you need to update it with new iterator returned by erase
it = my_vector_2.erase(it);
it = my_vector_2.insert(it,replacement);

There's an ready-made algorithm for your problem:
#include <algorithm>
#include <string>
#include <vector>
std::vector<std::string> v; // populate
std::replace(v.begin(), v.end(), "old", "new");

You are passing your std::vector as a value. In order to Change the std::vector you pass to the function, declare it as a reference
void replace(vector<string>& my_vector_2, string old, string replacement){ }
The & denotes that you pass your std::vector by reference and so you can access the object you passed.
And don't erase the element, simply replace it.

Related

For the erase-remove idiom, why is the second parameter necessary which points to the end of the container?

Consider the following code (taken from cppreference.com, slightly adapted):
#include <algorithm>
#include <string>
#include <iostream>
#include <cctype>
int main()
{
std::string str1 = " Text with some spaces";
str1.erase(std::remove(str1.begin(), str1.end(), ' '), str1.end());
std::cout << str1 << '\n';
return 0;
}
Why is the second parameter to erase neccessary? (I.e. str1.end() in this case.)
Why can't I just supply the iterators which are returned by remove to erase? Why do I have to tell it also about the last element of the container from which to erase?
The pitfall here is that you can also call erase without the second parameter but that produces the wrong result, obviously.
Are there use cases where I would not want to pass the end of the container as a second parameter to erase?
Is omitting the second parameter of erase for the erase-remove idiom always an error or could that be a valid thing to do?
std::remove returns one iterator; it's the new past-the-end iterator for the sequence. But when the sequence is managed by a container, the size of the container hasn't changed; std::remove shuffles the order of the elements in the sequence, but doesn't actually remove any of them.
To get rid of the elements in the container that are not part of the new sequence you call, of course, container.erase(). But the goal is to remove all the extra elements; calling container.erase() with only one iterator tells it to remove that element. To tell container.erase() to erase everything "from here to the end" you have to tell it both where "here" is and where the end is. So that means two iterators.
If it helps, think of the "remove/erase" idiom as two separate steps:
auto new_end = std::remove(str1.begin(), str1.end(), ' ');
str1.erase(new_end, str1.end());

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();

Why C++ map.insert() doesn't overwrite

In the code below:
#include <map>
#include <utility>
#include <iostream>
using namespace std;
int main(){
pair<int,int> p1(1,1);
pair<int,int> p2(1,2);
map<int,int> m;
m.insert(p1);
m.insert(p2);
cout << "Map value: "<< m.at(1) << endl;
}
It printed out : Map value: 1, why m.insert(p2) doesn't overwrite the previous entity in the map?
map.insert() only inserts if the container doesn't already contain an element with an equivalent key.
You should use operator[] instead:
m[p2.first] = p2.second;
In the std::map::insert reference it is said that:
Inserts element(s) into the container, if the container doesn't already contain an element with an equivalent key.
Update as of C++17 There is now the std::map::insert_or_assign() member function:
m.insert_or_assign(p1);
As the name suggests, if the key is already present then the value is assigned (and the key object kept) rather than erasing and freshly copy constructing the key and value. (So it's equivalent to the first of the two pre-C++17 snippets below.)
If you want an iterator pointing at the (new or updated) element, you again need to pick the value out of the returned pair. Since you're using C++17, you can now use a structured binding:
auto [it, wasInserted] = m.insert_or_assign(p1);
Before C++17 Putting together the other answers, if you want to avoid the assumption of being default constructable you get insert-with-overwrite code that looks like this:
auto itAndWasInserted = m.insert(p1);
if (!itAndWasInserted.second) {
*(itAndWasInserted.first) = p1;
}
In the above snippet, if the element is already present then the new value is assigned to it. That's usually what you want. If you instead want to construct rather than assign the new value, but still want to avoid a second seek (after you've erased the original value), you end up with this monster:
auto itAndWasInserted = m.insert(p1);
auto it = itAndWasInserted.first;
if (!itAndWasInserted.second) {
auto afterIt = m.erase(it);
auto newItAndWasInserted = m.insert(afterIt, p1); // Hint form of insert
it = newItAndWasInserted.first;
}
At the end of the code block, it is an iterator pointing at the just-inserted element.
Realistically, in most cases you probably just want to use yizzlez's suggestion of operator[], but I thought it would be good to note the theoretically best answer.
It doesn't overwrite. However if you check the return value, there is a std::pair<iterator, bool>. If bool is true, then it was inserted. If the bool is false, then it was not inserted because of a collision. At that point, you can then overwrite the data yourself by writing to the iterator.
This is supposed to happen. map.insert() will only insert elements into the container if it doesn't already contain any elements, so this will ignore the later value elements assigned to it.

Is it possible to iterate over an iterator?

I have a working program that capitalizes strings in a vector, using iterators:
vector<string> v7{ 10, "apples" };
for (auto vIterator= v7.begin(); vIterator!= v7.end(); ++vIterator){
auto word = *vIterator; //here
auto charIterator = word.begin();
*charIterator = toupper(*charIterator);
*vIterator = word; //also here, i guess i could just print `word` instead?
cout << *vIterator << endl;
}
My question is;
2nd line inside the loop # the comment, i had to save the pointer to the iterator to another string variable before i was able to iterate over it.
Iterating over the pointer like so
*vIterator.begin();
didn't seem to work.
Is this the correct practice, or am i missing something?
I'm new to the C languages, the concept behind pointer-like tools is quite hard to understand even if i can use them, and in this case it just feels like I'm doing it wrong.
Edit: It was a syntax error (*vIterator).begin();
It just didn't make sense why i'd have to save it to another variable before iterating over it, cheers.
Since you are using C++11 look how simpler your code can become using ranged loops like the example below:
std::vector<std::string> v(10, "apples");
for(auto &&word : v) {
word[0] = toupper(word[0]);
}
LIVE DEMO
Now as far as it concerns the (*vIterator.begin(); didn't seem to work.):
The dot operator (i.e., .) has a higher precedence than the dereference operator (i.e., *). Thus, *vIterator.begin() is interpreted as *(vIterator.begin()). The compiler rightfully complains because vIterator hasn't got a member begin().
Think of iterators as if they were pointers. The correct way to access the members of an object via a pointer/iterator pointing to it is either using the arrow operator (i.e., vIterator->begin()) or first dereference the pointer/iterator and then use the dot operator (i.e., (*vIterator).begin()).
So your code via the use of iterators would become:
std::vector<std::string> v(10, "apples");
for(auto it(v.begin()), ite(v.end()); it != ite; ++it) {
*(it->begin()) = toupper(*(it->begin()));
}
LIVE DEMO
The correct way to write *vIterator.begin(); is (*vIterator).begin(); or, more often, vIterator->begin();. Also note that you can also access the first character of a string directly (without having to iterate over it) as word[0].
A simple STL-ish way of doing it:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<string> v7{ 10, "apples" };
for_each(v7.begin(), v7.end(), [](string& word){word[0] = toupper(word[0]);});
}

Best way to delete a std::unique_ptr from a vector with a raw pointer?

So I have a vector like so:
std::vector<std::unique_ptr<SomeClass>> myVector;
Then I have another vector which contains raw pointers of SomeClass:
std::vector<SomeClass*> myOtherVector;
If there is an element inside myOtherVector it will also be inside myVector, so I want to go through each element in myOtherVector and remove the same element from myVector. Then clear out the vector. This is what I came up with:
for(size_t i = 0; i < myOtherVector.size(); i++)
{
myVector.erase(std::remove(myVector.begin(), myVector.end(), myOtherVector[i]), myVector.end());
}
myOtherVector.clear();
This produces a compile time error because myVector holds unique pointers but I'm giving the remove() function a raw pointer. This is where I need help because I don't know what the proper way to solve this problem would be. I changed the line to:
myVector.erase(std::remove(myVector.begin(), myVector.end(), std::unique_ptr<SomeClass>(myOtherVector[i])), myVector.end());
Frist of all this is incorrect because now I have two std::unique_ptrs referencing the same object. The element inside myVector contains a reference and the construction of the unique pointer in the above line is another reference. And I don't even know if constructing a new pointer to get the same type is conceptually the correct way to go about doing this. So then I changed the unique pointers to shared pointers:
std::vector<std::shared_ptr<SomeClass>> myVector;
std::vector<SomeClass*> myOtherVector;
for(size_t i = 0; i < myOtherVector.size(); i++)
{
myVector.erase(std::remove(myVector.begin(), myVector.end(), std::shared_ptr<SomeClass>(myOtherVector[i])), myVector.end());
}
myOtherVector.clear();
When I ran the application the myVector.erase() line resulted in a runtime error which said "ApplicationName.exe has triggered a breakpoint." upon clicking continue I got a debug assertion failure.
So obviously I'm doing something wrong, but I don't know what. What is the correct way to erase a smart pointer from a vector with a raw pointer?
This is how I would do it. Performance could be improved, but as long as it won't prove to be a bottleneck for your application, I would not bother with that. The algorithm is simple and clear.
It uses remove_if to selectively remove from the first container (myVector) all the elements pointing to objects that are pointed to by elements of the second container (myOtherVector); then, it clears the second container. The predicate is implemented through a lambda function:
#include <vector>
#include <memory>
#include <algorithm>
struct SomeClass { /* ... */ };
int main()
{
std::vector<std::unique_ptr<SomeClass>> myVector;
std::vector<SomeClass*> myOtherVector;
myVector.erase(
std::remove_if( // Selectively remove elements in the second vector...
myVector.begin(),
myVector.end(),
[&] (std::unique_ptr<SomeClass> const& p)
{ // This predicate checks whether the element is contained
// in the second vector of pointers to be removed...
return std::find(
myOtherVector.cbegin(),
myOtherVector.cend(),
p.get()
) != myOtherVector.end();
}),
myVector.end()
);
myOtherVector.clear();
}
std::unique_ptr has a member function, get, that returns the owned pointer.
Consider the following:
std::sort(myOtherVector.begin(), myOtherVector.end());
myVector.erase(std::remove_if(myVector.begin(), myVector.end(),
[&](std::unique_ptr<SomeClass> const& p) -> bool
{
return std::binary_search(myOtherVector.begin(), myOtherVector.end(),
p.get());
}));
myOtherVector.clear();
If you cant simplify your problem, how about std::set_difference or one of its kin (http://www.cplusplus.com/reference/algorithm/set_difference/)?
You would need to specify a compare function to get() the raw pointer from the unique_ptr