How to fix the crash happening during conditional copying - c++

I am trying to store only the alpha characters of a string 's' into my deque of characters using std::copy_if but is giving a crash during the copy statement.
std::string s{"Love"};
std::deque<char> dr;
std::deque<char>::reverse_iterator itr =std::copy_if(s.cbegin(),s.cend (),dr.rbegin(),[](char c){return isalpha(c);});
program.exe stopped working

The likely problem is that your deque isn't big enough to hold the characters you are copying. std::copy_if does not resize the deque (how could it since it only has an iterator to work with)? The usual answer is to use a special kind of iterator called a std::front_insert_iterator which will call push_front to grow the deque when elements are copied. You can create a std::front_insert_iterator with the function std::front_inserter. Like this
auto itr =
std::copy_if(s.cbegin(), s.cend(),
std::front_inserter(dr),
[](char c){return isalpha(c);});
PS there is also a corresponding std::back_insert_iterator and function std::back_inserter which are more commonly used. But std::front_inserter is appropriate for your case since it seems you want to reverse the sequence of characters when you insert them into the deque.

Related

Can I get a container object from an iterator?

std::vector<int> vec={1,2,3};
std::vector<int>::iterator it = vec.begin();
if(vec == get_vec_from_it(it)){
puts('sucesss');
}
std::vector<int> get_vec_from_it(std::vector<int>::iterator it){
/*?*/
}
How should I write get_vec_from_it function in the above example?
The basic idea is that iterators abstract away where the elements come from, there might not even be a container. Afaik there is a single type of iterator that "knows" its container and that is std::back_insert_iterator, though thats an exception. The container member is only protected so there is even a way to get the container from a std::back_insert_iterator, but thats not how it is meant to be used.
You can adance the iterator to get the next element, but you wouldn't know where to stop, because at some point you'll reach the end of the vector and there is no way to identify it. If you pass begin and end you can create a copy of the original vector:
std::vector<int> get_vec_from_it(std::vector<int>::iterator begin ,std::vector<int>::iterator end){
return {begin,end};
}
Though, thats just a different way to copy the vector and you need to know both begin and end.
I made a function that returns the iterator that points to the node but I couldn't write the stop condition in a for statement. So I wonder if the stop condition can be written like it!=get_vec_from_it(it).end()
Functions that work on a range of elements typically take a pair of iterators, first and last, to know where to stop (alternatively a first iterator and number of elements can be used). Your idea of using it!=get_vec_from_it(it).end() is overcomplicating the issue. Just pass vec.end() to the function and use that: it != end.
No.
You can create a vector from a pair of iterators, or an iterator and number of elements. Example:
std::vector<int>
get_vec_from_its(std::vector<int>::iterator first, std::vector<int>::iterator last){
return std::vector<int>(first, last);
}
// ...
if(vec == get_vec_from_it(vec.begin(), vec.end())){
The function is of course so trivial that I would recommend instead to use the constructor directly.

string erase appends elements with backward iterators

I am trying to remove every '0' in a std::string, starting from the back. According to this, there are multiple ways to .erase backward iterators.
The weird thing is that they all append numbers to the std::string, instead of erasing them!
Here is a little sample
//'str' will be "5.090000"
std::string str = std::to_string(5.09);
auto it = std::remove_if(str.rbegin(), str.rend(), [](char value) {
return value == '0';
});
1) Pre-C++11 way:
str.erase(--(it.base()));
2) C++11 way (1)
str.erase(std::next(it).base());
3) C++11 way (2)
std::advance(it, 1);
str.erase(it.base());
In all cases, str == "5.095.9". Why? Because as I see it, str should be 5.9, but it isn't...
My best guess is that I am doing something wrong with the backwards iterators, because if you split the string: 5.09 | 5.9, the first value has still the in-between '0', but not the last ones. The second value is actually the std::string I expected.
What I am doing wrong?
I made 2 errors in my approach:
erase called with only 1 iterator removes the element the iterator is pointing at, not from the iterator to the end (as I falsely assumed)
So str.erase(std::next(it).base(), str.end()); - this is still wrong, continue reading ;)
As #KerrekSB pointed out, I didn't read the docs carefully enough: Because I am using std::reverse_iterator, the elements get pushed back to the front! So, as it points to the new end iterator (which is BTW before the not-removed elements), I have to delete the range from the beginning (str.begin()) to it.base().
TL;DR
The new working version is: str.erase(str.begin(), it.base());

efficient way to remove a list of string from a big vector

I am using visual studio 2012 (windows) and I am trying to write an efficient c++ function to remove some words from a big vector of strings.
I am using stl algorithms. I am a c++ beginner so I am not sure that it is the best way to proceed. This is what I have did :
#include <algorithm>
#include <unordered_set>
using std::vector;
vector<std::string> stripWords(vector<std::string>& input,
std::tr1::unordered_set<std::string>& toRemove){
input.erase(
remove_if(input.begin(), input.end(),
[&toRemove](std::string x) -> bool {
return toRemove.find(x) != toRemove.end();
}));
return input;
}
But this don't work, It doesn't loop over all input vector.
This how I test my code:
vector<std::string> in_tokens;
in_tokens.push_back("removeme");
in_tokens.push_back("keep");
in_tokens.push_back("removeme1");
in_tokens.push_back("removeme1");
std::tr1::unordered_set<std::string> words;
words.insert("removeme");
words.insert("removeme1");
stripWords(in_tokens,words);
You need the two-argument form of erase. Don't outsmart yourself and write it on separate lines:
auto it = std::remove_if(input.begin(), input.end(),
[&toRemove](std::string x) -> bool
{ return toRemove.find(x) != toRemove.end(); });
input.erase(it, input.end()); // erases an entire range
Your approach using std::remove_if() is nearly the correct approach but it erases just one element. You need to use the two argument version of erase():
input.erase(
remove_if(input.begin(), input.end(),
[&toRemove](std::string x) -> bool {
return toRemove.find(x) != toRemove.end();
}), input.end());
std::remove_if() reorders the elements such that the kept elements are in the front of the sequence. It returns an iterator it to the first position which is to be considered the new end of the sequence, i.e., you need to erase the range [it, input.end()).
You've already gotten a couple of answers about how to this correctly.
Now, the question is whether you can make it substantially more efficient. The answer to that will depend on another question: do you care about the order of the strings in the vector?
If you can rearrange the strings in the vector without causing a problem, then you can make the removal substantially more efficient.
Instead of removing strings from the middle of the vector (which requires moving all the other strings over to fill in the hole) you can swap all the unwanted strings to the end of the vector, then remove them.
Especially if you're only removing a few strings from near the beginning of a large vector, this can improve efficiency a lot. Just for example, let's assume a string you want to remove is followed by 1000 other strings. With this, you end up swapping only two strings, then erasing the last one (which is fast). With your current method, you end up moving 1000 strings just to remove one.
Better still, even with fairly old compilers, you can expect swapping strings to be quite fast as a rule--typically faster than moving them would be (unless your compiler is new enough to support move assignment).

How to copy std::queue<char> into std::string?

I am struggling with this piece of code :
std::queue<char> output_queue;
std::string output_string
// put stuff into output_queue
while (!output_queue.empty())
{
output_string.insert(0,(output_queue.front()));
output_queue.pop();
}
I somehow can't do this since std::queue<char>::front() will return a char& and I can't put this into std::string.
You're missing an argument to make insert insert a character. You need to specify how many of that character:
output_string.insert(0, 1, output_queue.front());
If you want to make it easier on yourself, you can also use std::deque instead of std::queue and replace it with this:
std::deque<char> output_queue;
//fill output_queue in same way, but use push/pop_front/back instead of push/pop
std::string output_string(output_queue.begin(), output_queue.end());
output_queue.clear();
It would nearly be the same thing as now because your queue is actually using a std::deque by default under the hood. The deque, however, supports iterators, which makes this possible without ugly code that relies on the underlying storage.
You may use
output_string += (output_queue.front());
and then (after while) reverse it

Iterating over std::vector with lambda does not want to remove with remove_if

I have a small problem with lambda expression while using remove_if on std::vector
I have a following piece of code :
std::remove_if( openList.begin(), openList.end(),
[&](BoardNode& i){
std::cout<< i.getCoordinates() << std::endl;
std::cout<< currentNode.getCoordinates() << std::endl;
return i.getCoordinates() == currentNode.getCoordinates(); }
);
There is no compiler error with this, but the elements which return true from the above statement won't be removed from the vector;
I get printed on the screen e.g.
[5,5]
[5,5]
but the openList remains as it was.
std::remove_if doesn't erase anything from the vector, since it doesn't have access to it. Instead, it moves the elements you want to keep to the start of the range, leaving the remaining elements in a valid but unspecified state, and returns the new end.
You can use the "erase-remove" idiom to actually erase them from the vector:
openList.erase(
std::remove_if(
openList.begin(),
openList.end(),
[&](BoardNode& i){return i.getCoordinates() == currentNode.getCoordinates();}),
openList.end());
I think you intend to remove items from the vector. But what you do, would not really remove the items from the vector, which makes you think that the lambda doesn't work. You need to use erase() member function in conjunction with std::remove.
In other words, you have to use erase-remove idiom as:
v.erase(std::remove_if(v.begin(), v.end(), your-lambda-goes-here), v.end());
Removing is done by shifting the elements in the range in such a way
that elements to be erased are overwritten. The elements between the
old and the new ends of the range have unspecified values. An iterator
to the new end of the range is returned. Relative order of the
elements that remain is preserved.
http://en.cppreference.com/w/cpp/algorithm/remove
Also, check the example on that link.