To traverse a vector array normally, I use indexing(see code) instead of using an iterator. Today, I have tried to erase a vector element by indexing. I have tried several times and several ways, but each time there seems to be an error in the last line(in the compiler).
Please help me fix the code to erase an element by indexing. Thanks in advance.
vector<pair<int, pair<int, int> > > p;
p.push_back(make_pair(1,make_pair(2,4))) ;
p.push_back(make_pair(5,make_pair(6,7))) ;
for(int i = 0; i != p.size(); i++)
if(p[i].first == 1)
p.erase(i);
You can use
for(int i = 0; i != p.size(); ) // Don't increment here.
{
if(p[i].first == 1)
{
p.erase(p.begin() + i );
}
else
{
++i;
}
}
but it will be more idiomatic to use:
for ( auto iter = p.begin(); iter != p.end(); )
{
if( iter->first == 1 )
{
iter = p.erase(iter);
}
else
{
++iter;
}
}
Better still, use the erase-remove idiom.
p.erase(std::remove_if(p.begin(), p.end(),
[](std::pair<int, std::pair<int, int>> const& item) { return item.first == 1;}),
v.end());
It is not starightforward to make your code work as your indexing with i will be broken when you erase element - you need to increase i only when element is not erased.
for(int i = 0; i != p.size(); )
if(p[i].first == 1)
p.erase(p.begin() + i);
else
i++;
but you better use erase-remove idiom, as it is less error prone and more efficient (erasing in middle of vector is quite expensive):
p.erase( std::remove_if( p.begin(), p.end(), []( const auto &pair ) { return pair.first == 1; } ), p.end() );
std::remove_if()
Related
I've created a function to filter out the elements I don't like in a std::vector, in this case a vector of opencv contours. The code below looks like it would work, however it does not and I suspect it is because the indices are changed whenever I erase the indices, however I continue to the next i value iteration.
void FilterContours( std::vector<std::vector<cv::Point>> contours )
{
for ( int i = 0; i < contours.size(); i++ ) {
//Remove contours smaller than 5 from vector - example
if ( contours[i].size() < 5 ) {
contours.erase(contours.begin() + i);
continue;
}
//Other filtering...
}
return;
}
So the question is, would this work as intended (I don't think it does), and if not, how do I make it work as intended? Should I add a i -= 1 after the erase to maintain the correct index position?
Each time you erase() an element from a container, its size() decrements, and the indexes of the remaining elements are decremented as well. But you are incrementing your loop counter unconditionally, so every time you erase an element, you skip the next element that had followed it!
Also, you are passing your vector by-value, so you are operating on a copy of the vector, and the caller will not see any changes in the original vector.
The correct approach would be to either:
increment your index variable inside of the loop body only when an element is NOT erased. Leave the variable as-is when you DO erase an element:
void FilterContours( std::vector<std::vector<cv::Point>> &contours )
{
int i = 0;
while ( i < contours.size() ) {
if ( contours[i].size() < 5 ) {
contours.erase(contours.begin() + i);
continue;
}
//Other filtering...
++i;
}
}
use iterators instead of indexes:
void FilterContours( std::vector<std::vector<cv::Point>> &contours )
{
auto it = contours.begin();
while ( it != contours.end() ) {
if ( it->size() < 5 ) {
it = contours.erase(it);
continue;
}
//Other filtering...
++it;
}
}
use the erase-remove idiom:
void FilterContours( std::vector<std::vector<cv::Point>> &contours )
{
contours.erase(
std:::remove_if(
contours.begin(),
contours.end(),
[](const std::vector<cv::Point> &v)
{
if (v.size() < 5) return true;
//Other filtering...
return false;
}
),
contours.end()
);
}
Use the erase-remove idiom:
contours.erase(
std::remove_if(contours.begin(), contours.end(), [](const std::vector<cv::Point>& v){
return v.size() < 5;
}),
contours.end()
);
In general, when you iterate for removing, it is always better to iterate backwards:
for ( int i = contours.size()-1; i >=0; --i)
This will work but it results in slow code, because at each removal, the elements behind the removal will be copied/shifted back. For this reason, it is better, faster and more readable to use the dedicated idioms provided by the standard algorithm library, which are usually very optimized. In this case you have the erase/remove_if combination:
contours.erase(std::remove_if(contours.begin(), contours.end(), [](const auto& elem) { return elem.size() < 5; }), contours.end() );
A big advantage here is that std::remove_if() acts in a smarter way than the intuitive loop: it first "marks" the elements to remove, and then it compacts the remaining elements together. This process is O(N), while the (intuitive) loop is O(N^2), a huge difference for big vectors.
p.s.: the signature of your FilterContours function to take the vector by reference:
void FilterContours( std::vector<std::vector<cv::Point>>& contours ) // <-- by reference
Your FilterContours should take a reference, otherwise it won't have any impact on the caller.
void FilterContours(std::vector<std::vector<cv::Point>>& contours)
{
for (auto it = contours.begin(); it != contours.end(); )
{
if (it->size() < 5)
it = contours.erase(it);
else
++it;
}
}
Edit:
If you want to do it in the reverse order you could do:
void FilterContours_reverse(std::vector<std::vector<cv::Point>>& contours)
{
for (auto it = contours.rbegin(); it != contours.rend(); )
{
if (it->size() < 5)
contours.erase(std::next(it++).base());
else
++it;
}
}
I have a list that gets accessed in multiple places. There are some cases where I need to loop through the list from beginning to (end-n) elements and others where the whole list is accessed. I'm having trouble with the iterator arithmetic.
I want something that could do the following:
int n =10;
for (list<Term>::iterator itr = final.begin(); itr != (final.end()-n); itr++) {
//
}
does the following pseudo code make sense?
int N = myList.size() - n;
for (list<Term>::iterator itr = final.begin(),int length_reached=0; itr != (final.end() && length_reached<N; itr++,length_reached++) {
//
}
Using rbegin for me is not an option since I want the first instance of a match from the start of the list.
is there a better way of implementation here?
Since it's a list, random access is slow. Fortunately for you:
you're always starting at the beginning, and
std::list has a size() method
here's one way:
list<Term>::iterator itr = final.begin();
int to_do = std::max(0, int(final.size()) - n);
for ( ; to_do ; --to_do, ++itr )
{
// code here
}
Yes you can do this way
if ( n < final.size() )
{
auto m = final.size() - n;
for ( auto first = final.begin(); m != 0; ++first, --m )
{
//...
}
}
If the iterator itself can be changed in the loop then you can write the loop condition the following way
if ( n < final.size() )
{
auto m = final.size() - n;
for ( auto first = final.begin(); m != 0 && first != final.end(); ++first, --m )
{
//...
}
}
you can use reverse iterator and the std::advance
auto rit =final.rbegin();
std::advance(rit, n);
for (auto itr=final.begin(); itr!=rti.base(); ++itr) {
}
I have a vector with char type;
vector<char> a_chars;
and it contains 6 characters but 3 of them are duplicate.
How can I delete the duplicate char? Here is my current code and it doesnt compile:
for(int i = 0; i < a_chars.size(); i++)
{
char current = a_chars.at(i);
for(int j = i+1; j < a_chars.size(); j++)
{
if (current == a_chars.at(j))
{
a_chars.erase[j];
}
}
}
here is the compile error: "C:invalid types '[int]' for array subscript|"
EDIT:
Also I have tried with a_chars.erase(j) and a_chars.erase(a_chars.at(j) and still had compile error.
You can use std::unique with combination of vector::erase method (known as erase-remove idiom). The vector has to be, however, sorted.
vector<char> a_chars;
std::sort(a_chars.begin(), a_chars.end());
a_chars.erase(std::unique(a_chars.begin(), a_chars.end()), a_chars.end());
If you don't want to sort your vector. You can use following snippet to remove duplicites.
void remove_duplicities(std::vector<char>& vec)
{
for (auto iter = vec.begin(); iter != vec.end(); ++iter)
{
for (auto jter = std::next(iter); jter != vec.end(); ++jter)
{
if (*iter == *jter)
jter = std::prev(vec.erase(jter));
}
}
}
After some attempts I found out by my self.
It is not working with a_chars.erase[j]; nor a_chars.erase(j); neither a_chars.erase(a_chars.at(j));. All I had to do is make an iterator and make it same as 'j'.
vector<char>::iterator itr = a_chars.begin();
itr+=j;
a_chars.erase(itr);
I have a function that looks like this:
void myclass::myfunc()
{
int i;
for( std::vector<Foo>::iterator it = var.begin(), i = 0; it < var.end(); it++, i++ )
{
/* ... */
}
}
I'm getting this error:
Cannot convert from int to std::_Vector_iterator<>
What is wrong with this code?
The issue is with this part of the for loop:
std::vector<Foo>::iterator it = var.begin(), i = 0
C++ is interpreting this not as two comma-separated statements, but as a variable declaration for a variable named it that's an iterator, and as a new declaration of a variable i that's an iterator and initialized to 0. The error is because you can't initialize a vector iterator to 0.
To fix this, you'll need to hoist the definition outside of the loop:
int i = 0;
std::vector<Foo>::iterator it = var.begin();
for(; it < var.end(); it++, i++ )
{
// ...
}
Or move the initialization of i outside the loop:
int i = 0;
for( std::vector<Foo>::iterator it = var.begin(); it < var.end(); it++, i++ )
{
// ...
}
Here's another option. If you need to keep track of the index into the vector you're currently looking at, you could consider just using a counting for loop (without the iterator), or using just the iterator and using iterator subtraction to recover the index:
for (auto it = var.begin(); it != var.end(); ++it) {
// current position is it - var.begin();
}
And, finally, if you have a C++20-compliant compiler, you could eliminate the iterator entirely and use an enhanced for loop in the following way:
/* Requires C++20 */
for (int i = 0; Foo f: var) {
/* Do something worthwhile with f. */
i++;
}
Hope this helps!
You can do it like this:
int i = 0;
for( std::vector<int>::iterator it = v.begin(); it < v.end(); ++it, ++i){}
Get rid of the i=0; part (at least inside the loop header).
Also, if you insist on doing this at all, consider using:
for (auto it : var)
or:
for (auto it = var.begin(); it != var.end(); ++it)
...instead. Since you're using a random access iterator anyway, what you have as i is equivalent to it - var.begin(). Conversely, you could just use:
for (int i=0; i<var.size(); i++)
...and get an iterator when needed as var.begin() + i.
Depending on what's in the body of the loop, you probably want to get rid of the loop entirely, and replace it with an algorithm though.
Double iteration:
using std::begin; using std::end;
for (auto p = std::make_pair( begin(var), 0 ); p.first != end(var); ++p.first, ++p.second ) {
/* ... */
}
double iteration with named indexes/iterators:
using std::begin; using std::end;
int i;
std::vector<Foo>::iterator it;
for (std::tie( it, i ) = std::make_pair( begin(var), 0 ); it != end(var); ++it, ++i ) {
/* ... */
}
or bind the above pair on each iteration to better named variables:
using std::begin; using std::end;
for (auto p = std::make_pair( begin(var), 0 ); p.first != end(var); ++p.first, ++p.second ) {
auto const& it = p.first;
int i = p.second;
}
I have an assignment to merge two sorted vectors into a third sorted vector. I'm sure the solution to this problem is pretty simple, but my brain is fried at the moment and could use your help.
Basically vectors A and B have a size of 3. They will hold integers such as 1, 2, 3 and 4, 5, 6 respectively. I can't seem to get the syntax of my while loop correctly. I've tried making it a do/while loop, putting parentheses around the cases, and a few other things. It just doesn't seem to be reading the part after the &&.
The first for loop just makes the R vector have the right size. And the second for loop just displays the values of the R vector. The result should print out from 1-6, but I'm only seeing 1-3.
Any help would be appreciated!
void::combine(vector<int> A, vector<int> B, vector<int> R) {
int ia = 0, ib = 0, ir = 0;
for (int i = 0; i < A.size() + B.size(); i++) {
R.push_back(0);
}
while (ia != A.size() && ib != B.size()) {
if (A[ia] < B[ib]) {
R[ir] = A[ia];
ia += 1;
}
else {
R[ir] = B[ib];
ib += 1;
}
ir += 1;
}
for (int i = 0; i < R.size(); i++) {
cout << "L3[" << i << "] = " << R[i] << endl;
}
}
Assuming A contains [1,2,3] and B contains [4,5,6] as you say, this will not add any of the element in the B vector to the R vector.
This is because on the 4th iteration, ia == 3, and so the conjunctive condition is no longer true..
Try changing it to while(ia != A.size() || ib != B.size())
Probably you should avoid the loop altogether:
void combine(vector<int> const& A, vector<int> const& B, vector<int> & R) {
R.resize( A.size() + B.size() );
std::copy( A.begin(), A.end(), R.begin() );
std::copy( B.begin(), B.end(), R.begin()+A.size() );
std::sort( R.begin(), R.end() );
for ( int i = 0; i < R.size(); ++i )
{
cout << "L3[" << i << "] = " << R[i] << endl;
}
}
This is suboptimal as you are first copying and then ordering, but for a small size it will have no impact.
On the actual issues with your code: try to avoid pass-by-value, use resize instead of multiple push_back() to fix the size (note that if the R argument to your function was a non-empty vector then the final size would be bigger than you want). Consider using a return value instead of a reference argument --easier to read. You looped until the first of the counters reached the end, but left the rest of the elements in the other container without copying.
A manual implementation, using iterators would also be simpler:
typedef std::vector<int> vector_t;
vector_t combine( vector_t const & a, vector_t const & b ) {
vector_t r( a.size() + b.size() ); // [*]
vector_t::const_iterator ita = a.begin(), enda = a.end();
vector_t::const_iterator itb = b.begin(), endb = b.end();
vector_t::iterator itr = r.begin();
while ( ita != enda && itb != endb ) {
if ( *ita < *itb )
*itr++ = *ita++;
else
*itr++ = *itb++;
}
if ( ita != enda )
std::copy( ita, enda, itr );
else
std::copy( itb, endb, itr );
return r;
}
I don't know what you're trying to do in the while loop. But if you're just populating the vector R with the elements in A and B, without giving any regard to the order how they're added, then you can use insert function, as:
void::combine(const vector<int> &A, const vector<int> &B, vector<int>& R)
{
R.insert(R.end(), A.begin(), A.end());
R.insert(R.end(), B.begin(), B.end());
}
And if you want to order the vector R, then you can add the following line to the above function:
sort( R.begin(), R.end()); //sorts in increasing order.
You've to #include<algorithm> if you do so. If you want to sort in decreasing order then do this:
bool compare( int a, int b ) { return a > b; }
sort( R.begin(), R.end(), compare); //sorts in decreasing order.
First of all, this smells like a classic merge sort or a piece of it:
http://en.wikipedia.org/wiki/Merge_sort
The objective is to examine elements of the first vector, A, to elements of vector B, and append the elements to a resulting vector R, in order.
Thus if A[i] < B[j], append A[i] to R. With std::vector, there is no need to load the result vector with zeros before starting. See std::vector::push_back().
The following is untested code:
void Sort_Vector(const std::vector& A, const std::vector& B, std::vector& result)
{
unsigned int index_a = 0; // Point to first element in first vector.
unsigned int index_b = 0; // Point to first element in second vector.
result.clear(); // Clear all elements, so size == 0.
while ((index_a < A.size()) && (index_b < B.size())
{
if (A[index_a] < B[index_b])
{
result.push_back(A[index_a]);
++index_a;
}
else // B[index_b] <= A[index_a]
{
result.push_back(B[index_b]);
++index;
}
}
// Append any remaining elements to the result vector.
// Only one of the two loops should be executed,
// the other will fail the comparison expression
// and not execute.
for (; index_a < A.size(); ++index_a)
{
result.push_back(A[index_a]);
}
for (; index_b < B.size(); ++index_b)
{
result.push_back(B[index_b]);
}
return;
}
Notice the difference between my function's signature (parameters) and yours. I'm passing the source vectors as constant data and the result vector is passed by reference which enables my function to modify the caller's result vector.
Also, I'm lazy and using the std::vector::push_back() method. I believe this is more readable than assigning to an element in the result vector. The push_back method implies that I am appending to the vector. The assignment route may catch readers off-guard if they forget that a vector will expand in order resolve the assignment.
that's because of your && condition. as soon as ia == A.size(), the first part of the condition (ia != A.size()) will always evaluate to false and the ib part of your loop won't execute.
to fix it, you can change the condition to : (iA < A.size()) || (iB < B.size())
e.g. with 123 and 456 what is being done right now.
Initialize R to {0,0,0,0,0,0}
until all values from one vector are inserted into R compare candidates and insert smaller one into R, get next candidate from winner list.
and that's all - I don't know that is the purpose of the function if you'll write it I/We can provide solution otherwise just review 2nd point in above form.
The syntax is fine (the code wouldn't compile otherwise), but your loop condition isn't. Given your example A = { 1, 2, 3 } and B = { 4, 5, 6 }, the loop will enter the A[ia] < B[ib] branch three times and increment ia to 3. Once this happens, the first part of the loop condition, ia != A.size() won't be true, and so the loop ends.
Since you already know how many items you need to place in the result vector, I'd recommend replacing your while loop with a simple for one with the same body.