I am using Code::Blocks and here is my code below in c++.
void removeEvenLength(vector<string> &vec)
{
for (vector<string>::reverse_iterator i = vec.rbegin(); i != vec.rend(); ++i)
{
string word = *i;
if (word.length() % 2 == 0)
vec.erase(i);
}
}
This function takes a vector of strings and removes the word that is even letters long. I get an error that says erase needs 2 arguments but according to the c++ documentation, one should be okay. Do you know what is wrong?
If you absolutely must use erase() with a reverse iterator, here's how:
void removeEvenLength(vector<string> &vec)
{
for (vector<string>::reverse_iterator i = vec.rbegin(); i != vec.rend();)
{
string word = *i;
if (word.length() % 2 == 0) {
vector<string>::iterator newi = vec.erase(i.base() - 1);
i = vector<string>::reverse_iterator(newi);
} else {
++i;
}
}
}
But that's not the best way to remove all even-length strings in a vector, for a number of reasons. The way to do that is as follows (it's called the "erase-remove" idiom):
struct IsEven {
bool operator()(const string &s) const {
return (s.size() % 2) == 0;
}
};
vec.erase(
std::remove_if(vec.begin(), vec.end(), IsEven()),
vec.end()
);
Or in C++11 you can use a lambda instead of a separate functor:
vec.erase(
std::remove_if(
vec.begin(), vec.end(),
[](const string &s) { return (s.size() % 2) == 0; }
),
vec.end()
);
If you see e.g. this reference you will see that erase takes an iterator or a const_iterator, not a reverse_iterator. So you can't use it when iterating backwards.
Also, as suggested by chris in a comment, you might want to use std::remove_if instead. The linked reference contains an example how to use it.
Related
I want to clear a element from a vector using the erase method. But the problem here is that the element is not guaranteed to occur only once in the vector. It may be present multiple times and I need to clear all of them. My code is something like this:
void erase(std::vector<int>& myNumbers_in, int number_in)
{
std::vector<int>::iterator iter = myNumbers_in.begin();
std::vector<int>::iterator endIter = myNumbers_in.end();
for(; iter != endIter; ++iter)
{
if(*iter == number_in)
{
myNumbers_in.erase(iter);
}
}
}
int main(int argc, char* argv[])
{
std::vector<int> myNmbers;
for(int i = 0; i < 2; ++i)
{
myNmbers.push_back(i);
myNmbers.push_back(i);
}
erase(myNmbers, 1);
return 0;
}
This code obviously crashes because I am changing the end of the vector while iterating through it. What is the best way to achieve this? I.e. is there any way to do this without iterating through the vector multiple times or creating one more copy of the vector?
Use the remove/erase idiom:
std::vector<int>& vec = myNumbers; // use shorter name
vec.erase(std::remove(vec.begin(), vec.end(), number_in), vec.end());
What happens is that remove compacts the elements that differ from the value to be removed (number_in) in the beginning of the vector and returns the iterator to the first element after that range. Then erase removes these elements (whose value is unspecified).
Edit: While updating a dead link I discovered that starting in C++20 there are freestanding std::erase and std::erase_if functions that work on containers and simplify things considerably.
Calling erase will invalidate iterators, you could use:
void erase(std::vector<int>& myNumbers_in, int number_in)
{
std::vector<int>::iterator iter = myNumbers_in.begin();
while (iter != myNumbers_in.end())
{
if (*iter == number_in)
{
iter = myNumbers_in.erase(iter);
}
else
{
++iter;
}
}
}
Or you could use std::remove_if together with a functor and std::vector::erase:
struct Eraser
{
Eraser(int number_in) : number_in(number_in) {}
int number_in;
bool operator()(int i) const
{
return i == number_in;
}
};
std::vector<int> myNumbers;
myNumbers.erase(std::remove_if(myNumbers.begin(), myNumbers.end(), Eraser(number_in)), myNumbers.end());
Instead of writing your own functor in this case you could use std::remove:
std::vector<int> myNumbers;
myNumbers.erase(std::remove(myNumbers.begin(), myNumbers.end(), number_in), myNumbers.end());
In C++11 you could use a lambda instead of a functor:
std::vector<int> myNumbers;
myNumbers.erase(std::remove_if(myNumbers.begin(), myNumbers.end(), [number_in](int number){ return number == number_in; }), myNumbers.end());
In C++17 std::experimental::erase and std::experimental::erase_if are also available, in C++20 these are (finally) renamed to std::erase and std::erase_if (note: in Visual Studio 2019 you'll need to change your C++ language version to the latest experimental version for support):
std::vector<int> myNumbers;
std::erase_if(myNumbers, Eraser(number_in)); // or use lambda
or:
std::vector<int> myNumbers;
std::erase(myNumbers, number_in);
You can iterate using the index access,
To avoid O(n^2) complexity
you can use two indices, i - current testing index, j - index to
store next item and at the end of the cycle new size of the vector.
code:
void erase(std::vector<int>& v, int num)
{
size_t j = 0;
for (size_t i = 0; i < v.size(); ++i) {
if (v[i] != num) v[j++] = v[i];
}
// trim vector to new size
v.resize(j);
}
In such case you have no invalidating of iterators, complexity is O(n), and code is very concise and you don't need to write some helper classes, although in some case using helper classes can benefit in more flexible code.
This code does not use erase method, but solves your task.
Using pure stl you can do this in the following way (this is similar to the Motti's answer):
#include <algorithm>
void erase(std::vector<int>& v, int num) {
vector<int>::iterator it = remove(v.begin(), v.end(), num);
v.erase(it, v.end());
}
Depending on why you are doing this, using a std::set might be a better idea than std::vector.
It allows each element to occur only once. If you add it multiple times, there will only be one instance to erase anyway. This will make the erase operation trivial.
The erase operation will also have lower time complexity than on the vector, however, adding elements is slower on the set so it might not be much of an advantage.
This of course won't work if you are interested in how many times an element has been added to your vector or the order the elements were added.
There are std::erase and std::erase_if since C++20 which combines the remove-erase idiom.
std::vector<int> nums;
...
std::erase(nums, targetNumber);
or
std::vector<int> nums;
...
std::erase_if(nums, [](int x) { return x % 2 == 0; });
If you change your code as follows, you can do stable deletion.
void atest(vector<int>& container,int number_in){
for (auto it = container.begin(); it != container.end();) {
if (*it == number_in) {
it = container.erase(it);
} else {
++it;
}
}
}
However, a method such as the following can also be used.
void btest(vector<int>& container,int number_in){
container.erase(std::remove(container.begin(), container.end(), number_in),container.end());
}
If we must preserve our sequence’s order (say, if we’re keeping it sorted by some interesting property), then we can use one of the above. But if the sequence is just a bag of values whose order we don’t care about at all, then we might consider moving single elements from the end of the sequence to fill each new gap as it’s created:
void ctest(vector<int>& container,int number_in){
for (auto it = container.begin(); it != container.end(); ) {
if (*it == number_in) {
*it = std::move(container.back());
container.pop_back();
} else {
++it;
}
}
}
Below are their benchmark results:
CLang 15.0:
Gcc 12.2:
I want to check whether a given C++ STL list is palindromic or not?
bool isPalindromic(list <int> c);
int main(){
list<int> l;
l.push_front(12);
l.push_front(35);
l.push_front(34);
l.push_front(35);
l.push_front(12);
isPalindromic(l);
}
output : true
You can use std::equal to check if list from the beginning is equal to the list from the end. cppreference page for std::equal even has a example for that.
EDIT (as requested):
Basing on the example provided by cppreference:
std::equal(s.begin(), s.begin() + s.size()/2, s.rbegin());
The above line checks if a string is a palindrome. But in our case it will not work, since list's begin() returns BidirectionalIterator, and not the RandomAccessIterator string's begin() does, so we cannot do s.begin() + s.size()/2 part. To make it work we should change our code to:
bool is_palindrome(const std::list<int>& l) //use templated version std::list<T> if needed
{
return std::equal(l.begin(), l.end(), l.rbegin());
}
This of course is not perfect, since it iterates from the beginning to the end of the list, while it could just end after the middle, but it is simple and it works.
Palindrome means "the same read forwards and backwards", so just compare c with std::reverse(c).
Simple solution that avoids unnecessary comparisons performed by std::equal(begin, end, rbegin):
template<typename T>
bool is_palindrome(const std::list<T>& list) {
auto it1 = list.begin();
auto it2 = list.end();
if (list.size() % 2 == 0) {
while (it1 != it2)
if (!(*it1++ == *--it2))
return false;
} else {
while (it1 != --it2)
if (!(*it1++ == *it2))
return false;
}
return true;
}
Note that since C++11 std::list::size has constant time complexity.
Example:
std::list<int> l{12, 35, 34, 35, 12};
std::cout << std::boolalpha << is_palindrome(l); // Output: true
Demo with basic test cases
I have a vector of pointers, pointing to approx 10MB of packets. In that, from first 2MB, I wanna delete all those that matches my predicate. The problem here is remove_if iterates through the whole vector, even though its not required in my use case. Is there any other efficient way?
fn_del_first_2MB
{
uint32 deletedSoFar = 0;
uint32 deleteLimit = 2000000;
auto it = std::remove_if (cache_vector.begin(), cache_vector.end(),[deleteLimit,&deletedSoFar](const rc_vector& item){
if(item.ptr_rc->ref_count <= 0) {
if (deletedSoFar < deleteLimit) {
deletedSoFar += item.ptr_rc->u16packet_size;
delete(item.ptr_rc->packet);
delete(item.ptr_rc);
return true;
}
else
return false;
}
else
return false;
});
cache_vector.erase(it, cache_vector.end());
}
In the above code, once the deletedSoFar is greater than deleteLimit, any iteration more than that is unwanted.
Instead of cache_vector.end() put your own iterator marker myIter. With the remove_if option you should follow the erase-remove idiom. Here is an example that affects only the first 4 elements:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
size_t index = 4; // index is something you need to calculate
auto myIter = vec.begin() + index; // Your iterator instead of vec.end()
vec.erase(std::remove_if(vec.begin(), myIter, [](int x){return x < 3; }), myIter);
// modified vector:
for (const auto& a : vec)
{
std::cout << a << std::endl;
}
return 0;
}
You may use your own loop:
void fn_del_first_2MB()
{
const uint32 deleteLimit = 2000000;
uint32 deletedSoFar = 0;
auto dest = cache_vector.begin();
auto it = dest
for (; it != cache_vector.end(); ++it) {
const auto& item = *it;
if (item.ptr_rc->ref_count <= 0) {
deletedSoFar += item.ptr_rc->u16packet_size;
delete(item.ptr_rc->packet);
delete(item.ptr_rc);
if (deletedSoFar >= deleteLimit) {
++it;
break;
}
} else if (dest != it) {
*dest = std::move(*it);
++dest;
}
}
cache_vector.erase(dest, it);
}
There is no need for std::remove_if() to pass the .end() iterator as the second argument: as long as the first argument can reach the second argument by incrementing, any iterators can be passed.
There is somewhat of a complication as your condition depends on the accumulated size of the elements encountered so far. As it turns out, it looks as if std::remove_if() won't be used. Something like this should work (although I'm not sure if this use of std::find_if() is actually legal as it keeps changing the predicate):
std::size_t accumulated_size(0u);
auto send(std::find_if(cache_vector.begin(), cache_vector.end(),
[&](rc_vector const& item) {
bool rc(accumulated_size < delete_limit);
accumulated_size += item.ptr_rc->u16packet_size;
return rc;
});
std::for_each(cache_vector.begin(), send, [](rc_vector& item) {
delete(item.ptr_rc->packet);
delete(item.ptr_rc);
});
cache_vector.erase(cache_vector.begin(), send);
The std::for_each() could be folded into the use of std::find_if() as well but I prefer to keep things logically separate. For a sufficiently large sequence there could be a performance difference when the memory needs to be transferred to the cache twice. For the tiny numbers quoted I doubt that the difference can be measured.
I am trying to erase an entity from a list in two cases. In the first case the following worked just fine, where I have a list of pairs from which I want to erase a certain one:
bool remove(std::list<std::pair<std::string, size_t>>& myList, std::string const& name) {
for (auto i = myList.begin(); i != myList.end(); i++) {
if(i->first == name) {
myList.erase(i);
return true;
}
}
return false;
}
Here the pair gets removed from the list as it should, but when I have a list of structs it does not work as in the following:
void remove(std::list<myStruct>& myList , const std::string& name) {
for (auto i = myList.begin(); i != myList.end(); i++) {
if(i->name == name) {
myList.erase(i);
}
}
The program crashes in the erase part. Only if I plug in myList.erase(i++) then it works. Why is this??
Have I done something foul in the first case and it just happened to work, but then in the second case it does not? I can not understand the reason.
You're working on an invalidated iterator. That's undefined behavior. That's why erase returns a valid iterator.
If you want to only erase the first matching element, use find_if and then erase if the returned iterator isn't equal to end().
auto it = find_if(myList.begin(), myList.end(), [&name](auto const& p){
return p.first == name;
});
if(it == myList.end()){
return false;
}
myList.erase(it);
return true;
Otherwise, just use erase-remove idiom and be wary for its pitfalls (erase will happily accept 1 argument, but it'll call a different overload):
auto it = remove_if(myList.begin(), myList.end(), [&name](auto const& p){
return p.first == name;
});
myList.erase(it, myList.end());
The above is a generic version (will work if you change myList's type to vector for example), but as per ksfone's reply, std::list<T> implements member function template remove_if:
myList.remove_if([&name](auto const& p){
return p.first == name;
});
Your first loop removes an entry from the list and stops.
for (auto i = myList.begin(); i != myList.end(); i++) {
if(i->first == name) {
myList.erase(i);
return true;
}
}
while your second loop continues looking for matching elements:
for (auto i = myList.begin(); i != myList.end(); i++) {
if(i->name == name) {
myList.erase(i);
}
}
When you erase i from myList, i becomes invalid - we have no idea what it references now that the element it was talking about has gone away and may have been deleted, returned to the os and it's memory used by another thread.
The very next thing you do is i++ which is undefined behavior, since i is an invalid iterator.
The erase operator returns the next valid iterator, so you could write this:
for (auto i = myList.begin(); i != myList.end();) {
if(i->name == name)
i = myList.erase(i);
else
i++;
}
Or you could write:
void remove(std::list<myStruct>& myList , const std::string& name) {
myList.remove_if([&](const myStruct& it) { return it.name == name; });
}
or if your compiler supports C++14
void remove(std::list<myStruct>& myList , const std::string& name) {
myList.remove_if([&](auto& it) { return it.name == name; });
}
As in
#include <iostream>
#include <list>
#include <string>
struct A {
std::string name_;
A(const char* name) : name_(name) {}
};
void dump(const std::list<A>& list) {
for (auto&& a : list) {
std::cout << a.name_ << ' ';
}
std::cout << '\n';
}
int main() {
std::list<A> myList { { "hello", "pizza", "world", "pizza" } };
dump(myList);
const std::string name = "pizza";
myList.remove_if([&](const A& it){ return it.name_ == name; });
dump(myList);
return 0;
}
Live demo: http://ideone.com/SaWejv
erase() invalidates the erased iterator. The for loop then attempts to increment the invalidated iterator, resulting in undefined behavior, and a crash.
The correct way to do this, actually, would be:
i=erase(i);
rather than using post-increment.
I'll avoid repeating what others have stated and instead suggest a more elegant solution via the erase-remove idiom:
myList.erase(std::remove_if(myList.begin(), myList.end(), [&name](auto& el) {
return el.first == name;
}), myList.end());
The reason is that the iterator breaks when you remove an item, but in the first case you are returning back so the error never occurs in the next iteration. In any way it's not a matter of your struct.
In code below:
map<string,vector<int>> create(ifstream& in, const vector<string>& vec)
{
/*holds string and line numbers into which each string appears*/
typedef map<string,vector<int>> myMap;
typedef vector<string>::const_iterator const_iter;
myMap result;
string tmp;
unsigned int lineCounter = 0;
while(std::getline(in,tmp))
{
const_iter beg = vec.begin();
const_iter end = vec.end();
while (beg < end)
{
if ( tmp.find(*beg) != string::npos)
{
result[*beg].push_back(lineCounter);//THIS IS THE LINE I'M ASKING FOR
}
++beg;
}
++lineCounter;
}
return result;
}
How should I do it (check line commented in code) if I want to use insert method of map instead of using operator[]?
Thank you.
Seriously, I would not do it.
You are only going to complicate your code unnecessarily. You would need a call to the insert to generate the new element in the map and then modify it.
Just for the sake of it (avoiding the double lookup, but building an unnecessary empty vector):
result.insert( std::make_pair( *beg, std::vector<int>() ) )
.first->second.push_back( lineCounter );
EDIT: Real equivalent (functionality and performance):
std::map<std::string,std::vector<int> >::iterator it = result.upper_bound( *beg );
if ( it->first != *beg ) {
it = result.insert( it, std::make_pair( *beg, std::vector<int>() ) ).first;
}
it->second.push_back( lineCounter );
result.insert(pair<string,vector<int>>(*beg,100), vector<int>());
result[*beg].push_back(lineCounter);
This is more complicated (but slower too :-) than your current code, since that achieves two things with a single statement: (implicitly) inserts an empty array into the map, then adds a new element to the array.
map::insert returns a pair containing an iterator to the element (either the one just inserted or the existing one with that key) and a boolean indicating success or failure. You can then call iter->push_back(lineCounter) to add the new line number to the vector.
...and when you're done with all that, realize that this is exactly what operator[] does for you.