Keep duplicate elements separately - c++

I have a std::vector<std::string> textLines that contains a large number of, say city names. I remove the duplicates with:
using namespace std;
vector<string>::iterator iter;
sort(textLines.begin(), textLines.end());
iter = unique(textLines.begin(), textLines.end());
At this point the duplicate elements are all null (empty) strings at the end of the vector with the same size as before unique().
I remove them with:
textLines.resize(distance(textLines.begin(), iter));
This works OK but is there a way to keep the removed duplicates? It would be better (for me) if the duplicates were just moved to the end and not replaced by empty strings.
The new end is pointed out by iter, returned from unique() so there is no problem finding the new end of the vector.
Put another way, I want to know which lines had duplicates and which had not.

You can do this very simply without actually changing your logic drastically.
You can store duplicates in another container, that is captured by the comparison predicate passed to unique():
vector<string> duplicates;
auto iter = unique(textLines.begin(), textLines.end(), [&duplicates](auto& first, auto& second) -> bool {
if (first == second)
{
duplicates.push_back(second);
return true;
}
return false;
});
Live example: here.

With this solution you need additional memory to store elements count.
vector<string>::iterator iter;
vector<string> v{ "a", "b", "a", "t", "a", "g", "t" };
sort(v.begin(), v.end());
// Find number of distinct elements
int count = 1;
auto current = v.cbegin();
for (auto i = v.cbegin() + 1; i < v.cend(); ++i) {
if (*i != *current) {
++count;
current = i;
}
}
// Count every entry
vector<int> vCount(count);
auto currentCount = vCount.begin();
++*currentCount;
for (size_t i = 1; i < v.size(); ++i) {
if (v[i] == v[i-1]) ++*currentCount;
else *++currentCount = 1;
}
iter = unique(v.begin(), v.end());

You could always write your own function, which is advisable for cases like yours where you have a specific request. Something like:
//Define a "bool has(const vector &v, int element)" function before
vector<string> nonDuplicates;
vector<string> duplicates;
for (auto i : textList) {
if (has(nonDupicates, i)) {
duplicates.push(i);
}
else {
nonDuplicates.push(i);
}
}
This is not a very elegant, or fast, way to do it, so you can probably find a better way, but if you do do it this way, use a binary search for has(), if you have sorted it

Related

How to repair SigSegV [duplicate]

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:

c++ Remove element from list and assign it to an object variable

I have a list of vector of Data (object) and I need to iterate through the list, find the biggest vector, remove it from the list and assign it to a new variable (which is a vector of Data). I am having problems during execution (it compiles ok but then stops working). How can I get the element without destroying it so I can manipulate later?
This is the code:
int biggestIndex = 0, biggestValue = -1;
i = 0;
list< vector<Data> >::iterator it;
for (it = (myList).begin(); it!= (myList).end(); it++) {
if ((*it).size() > biggerSize) {
biggestIndex = i;
biggestValue = basePList.size();
}
i++;
}
it = (myList).begin();
advance(it,biggestIndex);
vector<Data> partition = (vector<Data>) *it;
auto biggest = thelist.begin();
for (auto itr = thelist.begin() ; itr != thelist.end(); itr++) {
if (itr->size() > biggest->size()) {
biggest = itr;
}
}
vector<int> thebiggest = *biggest;
This of course needs to be compiled with at least C++11 extensions enabled, so add -std=c++11 or higher to your g++ command.
auto longest = *max_element(begin(myList),
end(myList),
[](const vector<Data>& v1, const vector<Data>& v2)
{return v1.size() < v2.size();});

Keeping track of removed elements using std::remove_if

I want to remove some elements from a vector and am using remove_if algorithm to do this. But I want to keep track of the removed elements so that I can perform some operation on them later. I tried this with the following code:
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
struct IsEven
{
bool operator()(int n)
{
if(n % 2 == 0)
{
evens.push_back(n);
return true;
}
return false;
}
vector<int> evens;
};
int main(int argc, char **argv)
{
vector<int> v;
for(int i = 0; i < 10; ++i)
{
v.push_back(i);
}
IsEven f;
vector<int>::iterator newEnd = remove_if(v.begin(), v.end(), f);
for(vector<int>::iterator it = f.evens.begin(); it != f.evens.end(); ++it)
{
cout<<*it<<"\n";
}
v.erase(newEnd, v.end());
return 0;
}
But this doesn't work as remove_if accepts the copy of my functor object, so the the stored evens vector is not accessible. What is the correct way of achieving this?
P.S. : The example, with even and odds is just for example sake, my real code is somethinf different. So don't suggest a way to identify even or odds differently.
The solution is not remove_if, but it's cousin partial_sort partition. The difference is that remove_if only guarantees that [begin, middle) contains the matching elements, but partition also guarantees that [middle, end) contains the elements which didn't match the predicate.
So, your example becomes just (note that evens is no longer needed):
vector<int>::iterator newEnd = partition(v.begin(), v.end(), f);
for(vector<int>::iterator it = newEnd; it != v.end(); ++it)
{
cout<<*it<<"\n";
}
v.erase(newEnd, v.end());
Your best bet is std::partition() which will rearrange all elts in the sequence such as all elts for which your predicate return true will precede those for which it returns false.
Exemple:
vector<int>::iterator bound = partition (v.begin(), v.end(), IsEven);
std::cout << "Even numbers:" << std::endl;
for (vector<int>::iterator it = v.begin(); it != bound; ++it)
std::cout << *it << " ";
std::cout << "Odd numbers:" << std::endl;
for (vector<int>::iterator it = bound; it != v.end(); ++it)
std::cout << *it << " ";
You can avoid copying your functor (i.e. pass by value) if you pass ist by reference like this:
vector<int>::iterator newEnd = remove_if(v.begin(), v.end(),
boost::bind<int>(boost::ref(f), _1));
If you can't use boost, the same is possible with std::ref. I tested the code above and it works as expected.
An additional level of indirection. Declare the vector locally, and
have IsEven contain a copy to it. It's also possible for IsEven to
own the vector, provided that it is dynamically allocated and managed by
a shared_ptr. In practice, I've generally found the local variable
plus pointer solution more convenient. Something like:
class IsEven
{
std::vector<int>* myEliminated;
public:
IsEven( std::vector<int>* eliminated = NULL )
: myEliminated( eliminated )
{
}
bool
operator()( int n ) const
{
bool results = n % 2 == 0;
if ( results && myEliminated != NULL ) {
myEliminated->push_back( n );
}
return results;
}
}
Note that this also allows the operator()() function to be const. I
think this is formally required (although I'm not sure).
The problem that I see with the code is that the evens vector that you create inside the struct gets created everytime the remove_if algorithm calls it. So no matter if you pass in a functor to remove_if it will create a new vector each time. So once the last element is removed and when the function call ends and comes out of the function the f.evens will always fetch an empty vector. This could be sorted in two ways,
Replace the struct with a class and declare evens as static (if that is what you wanted)
Or you could make evens global. I wouldn't personally recommend that( it makes the code bad, say no to globals unless you really need them).
Edit:
As suggested by nabulke you could also std::ref something likke this, std::ref(f). This prevents you from making the vector global and avoids for unnecessary statics.
A sample of making it global is as follows,
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
vector<int> evens;
struct IsEven
{
bool operator()(int n)
{
if(n % 2 == 0)
{
evens.push_back(n);
return true;
}
return false;
}
};
int main(int argc, char **argv)
{
vector<int> v;
for(int i = 0; i < 10; ++i)
{
v.push_back(i);
}
IsEven f;
vector<int>::iterator newEnd = remove_if(v.begin(), v.end(), f);
for(vector<int>::iterator it = evens.begin(); it != evens.end(); ++it)
{
cout<<*it<<"\n";
}
v.erase(newEnd, v.end());
return 0;
}
This code seems to work just fine for me. Let me know if this is not what you wanted.
You may have another solution; only if you don't need to remove elts in the same time (do you?). With std::for_each() which returns a copy of your functor. Exemple:
IsEven result = std::for_each(v.begin(), v.end(), IsEven());
// Display the even numbers.
std::copy(result.evens.begin(), result.evens.end(), std::ostream_iterator<int> (cout, "\n"));
Take note that it is always better to create unnamed variables in c++ when possible. Here that solution does not exactly answer your primary issue (removing elts from the source container), but it reminds everyone that std::for_each() returns a copy of your functor. :-)

Erasing elements in std::vector by using indexes

I've a std::vector<int> and I need to remove all elements at given indexes (the vector usually has high dimensionality). I would like to know, which is the most efficient way to do such an operation having in mind that the order of the original vector should be preserved.
Although, I found related posts on this issue, some of them needed to remove one single element or multiple elements where the remove-erase idiom seemed to be a good solution.
In my case, however, I need to delete multiple elements and since I'm using indexes instead of direct values, the remove-erase idiom can't be applied, right?
My code is given below and I would like to know if it's possible to do better than that in terms of efficiency?
bool find_element(const vector<int> & vMyVect, int nElem){
return (std::find(vMyVect.begin(), vMyVect.end(), nElem)!=vMyVect.end()) ? true : false;
}
void remove_elements(){
srand ( time(NULL) );
int nSize = 20;
std::vector<int> vMyValues;
for(int i = 0; i < nSize; ++i){
vMyValues.push_back(i);
}
int nRandIdx;
std::vector<int> vMyIndexes;
for(int i = 0; i < 6; ++i){
nRandIdx = rand() % nSize;
vMyIndexes.push_back(nRandIdx);
}
std::vector<int> vMyResult;
for(int i=0; i < (int)vMyValues.size(); i++){
if(!find_element(vMyIndexes,i)){
vMyResult.push_back(vMyValues[i]);
}
}
}
I think it could be more efficient, if you just just sort your indices and then delete those elements from your vector from the highest to the lowest. Deleting the highest index on a list will not invalidate the lower indices you want to delete, because only the elements higher than the deleted ones change their index.
If it is really more efficient will depend on how fast the sorting is. One more pro about this solultion is, that you don't need a copy of your value vector, you can work directly on the original vector. code should look something like this:
... fill up the vectors ...
sort (vMyIndexes.begin(), vMyIndexes.end());
for(int i=vMyIndexes.size() - 1; i >= 0; i--){
vMyValues.erase(vMyValues.begin() + vMyIndexes[i])
}
to avoid moving the same elements many times, we can move them by ranges between deleted indexes
// fill vMyIndexes, take care about duplicated values
vMyIndexes.push_back(-1); // to handle range from 0 to the first index to remove
vMyIndexes.push_back(vMyValues.size()); // to handle range from the last index to remove and to the end of values
std::sort(vMyIndexes.begin(), vMyIndexes.end());
std::vector<int>::iterator last = vMyValues.begin();
for (size_t i = 1; i != vMyIndexes.size(); ++i) {
size_t range_begin = vMyIndexes[i - 1] + 1;
size_t range_end = vMyIndexes[i];
std::copy(vMyValues.begin() + range_begin, vMyValues.begin() + range_end, last);
last += range_end - range_begin;
}
vMyValues.erase(last, vMyValues.end());
P.S. fixed a bug, thanks to Steve Jessop that patiently tried to show me it
Erase-remove multiple elements at given indices
Update: after the feedback on performance from #kory, I've modified the algorithm not to use flagging and move/copy elements in chunks (not one-by-one).
Notes:
indices need to be sorted and unique
uses std::move (replace with std::copy for c++98):
Github
Live example
Code:
template <class ForwardIt, class SortUniqIndsFwdIt>
inline ForwardIt remove_at(
ForwardIt first,
ForwardIt last,
SortUniqIndsFwdIt ii_first,
SortUniqIndsFwdIt ii_last)
{
if(ii_first == ii_last) // no indices-to-remove are given
return last;
typedef typename std::iterator_traits<ForwardIt>::difference_type diff_t;
typedef typename std::iterator_traits<SortUniqIndsFwdIt>::value_type ind_t;
ForwardIt destination = first + static_cast<diff_t>(*ii_first);
while(ii_first != ii_last)
{
// advance to an index after a chunk of elements-to-keep
for(ind_t cur = *ii_first++; ii_first != ii_last; ++ii_first)
{
const ind_t nxt = *ii_first;
if(nxt - cur > 1)
break;
cur = nxt;
}
// move the chunk of elements-to-keep to new destination
const ForwardIt source_first =
first + static_cast<diff_t>(*(ii_first - 1)) + 1;
const ForwardIt source_last =
ii_first != ii_last ? first + static_cast<diff_t>(*ii_first) : last;
std::move(source_first, source_last, destination);
// std::copy(source_first, source_last, destination) // c++98 version
destination += source_last - source_first;
}
return destination;
}
Usage example:
std::vector<int> v = /*...*/; // vector to remove elements from
std::vector<int> ii = /*...*/; // indices of elements to be removed
// prepare indices
std::sort(ii.begin(), ii.end());
ii.erase(std::unique(ii.begin(), ii.end()), ii.end());
// remove elements at indices
v.erase(remove_at(v.begin(), v.end(), ii.begin(), ii.end()), v.end());
What you can do is split the vector (actually any non-associative container) in two
groups, one corresponding to the indices to be erased and one containing the rest.
template<typename Cont, typename It>
auto ToggleIndices(Cont &cont, It beg, It end) -> decltype(std::end(cont))
{
int helpIndx(0);
return std::stable_partition(std::begin(cont), std::end(cont),
[&](typename Cont::value_type const& val) -> bool {
return std::find(beg, end, helpIndx++) != end;
});
}
you can then delete from (or up to) the split point to erase (keep only)
the elements corresponding to the indices
std::vector<int> v;
v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
int ar[] = { 2, 0, 4 };
v.erase(ToggleIndices(v, std::begin(ar), std::end(ar)), v.end());
If the 'keep only by index' operation is not needed you can use remove_if insted of stable_partition (O(n) vs O(nlogn) complexity)
To work for C arrays as containers the lambda function should be
[&](decltype(*(std::begin(cont))) const& val) -> bool
{ return std::find(beg, end, helpIndx++) != end; }
but then the .erase() method is no longer an option
If you want to ensure that every element is only moved once, you can simply iterate through each element, copy those that are to remain into a new, second container, do not copy the ones you wish to remove, and then delete the old container and replace it with the new one :)
This is an algorithm based on Andriy Tylychko's answer so that this can make it easier and faster to use the answer, without having to pick it apart. It also removes the need to have -1 at the beginning of the indices list and a number of items at the end. Also some debugging code to make sure the indices are valid (sorted and valid index into items).
template <typename Items_it, typename Indices_it>
auto remove_indices(
Items_it items_begin, Items_it items_end
, Indices_it indices_begin, Indices_it indices_end
)
{
static_assert(
std::is_same_v<std::random_access_iterator_tag
, typename std::iterator_traits<Items_it>::iterator_category>
, "Can't remove items this way unless Items_it is a random access iterator");
size_t indices_size = std::distance(indices_begin, indices_end);
size_t items_size = std::distance(items_begin, items_end);
if (indices_size == 0) {
// Nothing to erase
return items_end;
}
// Debug check to see if the indices are already sorted and are less than
// size of items.
assert(indices_begin[0] < items_size);
assert(std::is_sorted(indices_begin, indices_end));
auto last = items_begin;
auto shift = [&last, &items_begin](size_t range_begin, size_t range_end) {
std::copy(items_begin + range_begin, items_begin + range_end, last);
last += range_end - range_begin;
};
size_t last_index = -1;
for (size_t i = 0; i != indices_size; ++i) {
shift(last_index + 1, indices_begin[i]);
last_index = indices_begin[i];
}
shift(last_index + 1, items_size);
return last;
}
Here is an example of usage:
template <typename T>
std::ostream& operator<<(std::ostream& os, std::vector<T>& v)
{
for (auto i : v) {
os << i << " ";
}
os << std::endl;
return os;
}
int main()
{
using std::begin;
using std::end;
std::vector<int> items = { 1, 3, 6, 8, 13, 17 };
std::vector<int> indices = { 0, 1, 2, 3, 4 };
std::cout << items;
items.erase(
remove_indices(begin(items), end(items), begin(indices), end(indices))
, std::end(items)
);
std::cout << items;
return 0;
}
Output:
1 3 6 8 13 17
17
The headers required are:
#include <iterator>
#include <vector>
#include <iostream> // only needed for output
#include <cassert>
#include <type_traits>
And a Demo can be found on godbolt.org.

Can you remove elements from a std::list while iterating through it?

I've got code that looks like this:
for (std::list<item*>::iterator i=items.begin();i!=items.end();i++)
{
bool isActive = (*i)->update();
//if (!isActive)
// items.remove(*i);
//else
other_code_involving(*i);
}
items.remove_if(CheckItemNotActive);
I'd like remove inactive items immediately after update them, inorder to avoid walking the list again. But if I add the commented-out lines, I get an error when I get to i++: "List iterator not incrementable". I tried some alternates which didn't increment in the for statement, but I couldn't get anything to work.
What's the best way to remove items as you are walking a std::list?
You have to increment the iterator first (with i++) and then remove the previous element (e.g., by using the returned value from i++). You can change the code to a while loop like so:
std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
bool isActive = (*i)->update();
if (!isActive)
{
items.erase(i++); // alternatively, i = items.erase(i);
}
else
{
other_code_involving(*i);
++i;
}
}
You want to do:
i= items.erase(i);
That will correctly update the iterator to point to the location after the iterator you removed.
You need to do the combination of Kristo's answer and MSN's:
// Note: Using the pre-increment operator is preferred for iterators because
// there can be a performance gain.
//
// Note: As long as you are iterating from beginning to end, without inserting
// along the way you can safely save end once; otherwise get it at the
// top of each loop.
std::list< item * >::iterator iter = items.begin();
std::list< item * >::iterator end = items.end();
while (iter != end)
{
item * pItem = *iter;
if (pItem->update() == true)
{
other_code_involving(pItem);
++iter;
}
else
{
// BTW, who is deleting pItem, a.k.a. (*iter)?
iter = items.erase(iter);
}
}
Of course, the most efficient and SuperCool® STL savy thing would be something like this:
// This implementation of update executes other_code_involving(Item *) if
// this instance needs updating.
//
// This method returns true if this still needs future updates.
//
bool Item::update(void)
{
if (m_needsUpdates == true)
{
m_needsUpdates = other_code_involving(this);
}
return (m_needsUpdates);
}
// This call does everything the previous loop did!!! (Including the fact
// that it isn't deleting the items that are erased!)
items.remove_if(std::not1(std::mem_fun(&Item::update)));
I have sumup it, here is the three method with example:
1. using while loop
list<int> lst{4, 1, 2, 3, 5};
auto it = lst.begin();
while (it != lst.end()){
if((*it % 2) == 1){
it = lst.erase(it);// erase and go to next
} else{
++it; // go to next
}
}
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
2. using remove_if member funtion in list:
list<int> lst{4, 1, 2, 3, 5};
lst.remove_if([](int a){return a % 2 == 1;});
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
3. using std::remove_if funtion combining with erase member function:
list<int> lst{4, 1, 2, 3, 5};
lst.erase(std::remove_if(lst.begin(), lst.end(), [](int a){
return a % 2 == 1;
}), lst.end());
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
4. using for loop , should note update the iterator:
list<int> lst{4, 1, 2, 3, 5};
for(auto it = lst.begin(); it != lst.end();++it){
if ((*it % 2) == 1){
it = lst.erase(it); erase and go to next(erase will return the next iterator)
--it; // as it will be add again in for, so we go back one step
}
}
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
Use std::remove_if algorithm.
Edit:
Work with collections should be like:
prepare collection.
process collection.
Life will be easier if you won't mix this steps.
std::remove_if. or list::remove_if ( if you know that you work with list and not with the TCollection )
std::for_each
The alternative for loop version to Kristo's answer.
You lose some efficiency, you go backwards and then forward again when deleting but in exchange for the extra iterator increment you can have the iterator declared in the loop scope and the code looking a bit cleaner. What to choose depends on priorities of the moment.
The answer was totally out of time, I know...
typedef std::list<item*>::iterator item_iterator;
for(item_iterator i = items.begin(); i != items.end(); ++i)
{
bool isActive = (*i)->update();
if (!isActive)
{
items.erase(i--);
}
else
{
other_code_involving(*i);
}
}
Here's an example using a for loop that iterates the list and increments or revalidates the iterator in the event of an item being removed during traversal of the list.
for(auto i = items.begin(); i != items.end();)
{
if(bool isActive = (*i)->update())
{
other_code_involving(*i);
++i;
}
else
{
i = items.erase(i);
}
}
items.remove_if(CheckItemNotActive);
Removal invalidates only the iterators that point to the elements that are removed.
So in this case after removing *i , i is invalidated and you cannot do increment on it.
What you can do is first save the iterator of element that is to be removed , then increment the iterator and then remove the saved one.
If you think of the std::list like a queue, then you can dequeue and enqueue all the items that you want to keep, but only dequeue (and not enqueue) the item you want to remove. Here's an example where I want to remove 5 from a list containing the numbers 1-10...
std::list<int> myList;
int size = myList.size(); // The size needs to be saved to iterate through the whole thing
for (int i = 0; i < size; ++i)
{
int val = myList.back()
myList.pop_back() // dequeue
if (val != 5)
{
myList.push_front(val) // enqueue if not 5
}
}
myList will now only have numbers 1-4 and 6-10.
Iterating backwards avoids the effect of erasing an element on the remaining elements to be traversed:
typedef list<item*> list_t;
for ( list_t::iterator it = items.end() ; it != items.begin() ; ) {
--it;
bool remove = <determine whether to remove>
if ( remove ) {
items.erase( it );
}
}
PS: see this, e.g., regarding backward iteration.
PS2: I did not thoroughly tested if it handles well erasing elements at the ends.
You can write
std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
bool isActive = (*i)->update();
if (!isActive) {
i = items.erase(i);
} else {
other_code_involving(*i);
i++;
}
}
You can write equivalent code with std::list::remove_if, which is less verbose and more explicit
items.remove_if([] (item*i) {
bool isActive = (*i)->update();
if (!isActive)
return true;
other_code_involving(*i);
return false;
});
The std::vector::erase std::remove_if idiom should be used when items is a vector instead of a list to keep compexity at O(n) - or in case you write generic code and items might be a container with no effective way to erase single items (like a vector)
items.erase(std::remove_if(begin(items), end(items), [] (item*i) {
bool isActive = (*i)->update();
if (!isActive)
return true;
other_code_involving(*i);
return false;
}));
do while loop, it's flexable and fast and easy to read and write.
auto textRegion = m_pdfTextRegions.begin();
while(textRegion != m_pdfTextRegions.end())
{
if ((*textRegion)->glyphs.empty())
{
m_pdfTextRegions.erase(textRegion);
textRegion = m_pdfTextRegions.begin();
}
else
textRegion++;
}
I'd like to share my method. This method also allows the insertion of the element to the back of the list during iteration
#include <iostream>
#include <list>
int main(int argc, char **argv) {
std::list<int> d;
for (int i = 0; i < 12; ++i) {
d.push_back(i);
}
auto it = d.begin();
int nelem = d.size(); // number of current elements
for (int ielem = 0; ielem < nelem; ++ielem) {
auto &i = *it;
if (i % 2 == 0) {
it = d.erase(it);
} else {
if (i % 3 == 0) {
d.push_back(3*i);
}
++it;
}
}
for (auto i : d) {
std::cout << i << ", ";
}
std::cout << std::endl;
// result should be: 1, 3, 5, 7, 9, 11, 9, 27,
return 0;
}
I think you have a bug there, I code this way:
for (std::list<CAudioChannel *>::iterator itAudioChannel = audioChannels.begin();
itAudioChannel != audioChannels.end(); )
{
CAudioChannel *audioChannel = *itAudioChannel;
std::list<CAudioChannel *>::iterator itCurrentAudioChannel = itAudioChannel;
itAudioChannel++;
if (audioChannel->destroyMe)
{
audioChannels.erase(itCurrentAudioChannel);
delete audioChannel;
continue;
}
audioChannel->Mix(outBuffer, numSamples);
}