Multiset erasing only one instance [duplicate] - c++

How do I remove from a map while iterating it? like:
std::map<K, V> map;
for(auto i : map)
if(needs_removing(i))
// remove it from the map
If I use map.erase it will invalidate the iterators

The standard associative-container erase idiom:
for (auto it = m.cbegin(); it != m.cend() /* not hoisted */; /* no increment */)
{
if (must_delete)
{
m.erase(it++); // or "it = m.erase(it)" since C++11
}
else
{
++it;
}
}
Note that we really want an ordinary for loop here, since we are modifying the container itself. The range-based loop should be strictly reserved for situations where we only care about the elements. The syntax for the RBFL makes this clear by not even exposing the container inside the loop body.
Edit. Pre-C++11, you could not erase const-iterators. There you would have to say:
for (std::map<K,V>::iterator it = m.begin(); it != m.end(); ) { /* ... */ }
Erasing an element from a container is not at odds with constness of the element. By analogy, it has always been perfectly legitimate to delete p where p is a pointer-to-constant. Constness does not constrain lifetime; const values in C++ can still stop existing.

I personally prefer this pattern which is slightly clearer and simpler, at the expense of an extra variable:
for (auto it = m.cbegin(), next_it = it; it != m.cend(); it = next_it)
{
++next_it;
if (must_delete)
{
m.erase(it);
}
}
Advantages of this approach:
the for loop incrementor makes sense as an incrementor;
the erase operation is a simple erase, rather than being mixed in with increment logic;
after the first line of the loop body, the meaning of it and next_it remain fixed throughout the iteration, allowing you to easily add additional statements referring to them without headscratching over whether they will work as intended (except of course that you cannot use it after erasing it).

Assuming C++11, here is a one-liner loop body, if this is consistent with your programming style:
using Map = std::map<K,V>;
Map map;
// Erase members that satisfy needs_removing(itr)
for (Map::const_iterator itr = map.cbegin() ; itr != map.cend() ; )
itr = needs_removing(itr) ? map.erase(itr) : std::next(itr);
A couple of other minor style changes:
Show declared type (Map::const_iterator) when possible/convenient, over using auto.
Use using for template types, to make ancillary types (Map::const_iterator) easier to read/maintain.

The C++20 draft contains the convenience function std::erase_if.
So you can use that function to do it as a one-liner.
std::map<K, V> map_obj;
//calls needs_removing for each element and erases it, if true was reuturned
std::erase_if(map_obj,needs_removing);
//if you need to pass only part of the key/value pair
std::erase_if(map_obj,[](auto& kv){return needs_removing(kv.first);});

In short "How do I remove from a map while iterating it?"
With old map impl: You can't
With new map impl: almost as #KerrekSB suggested. But there are some syntax issues in what he posted.
From GCC map impl (note GXX_EXPERIMENTAL_CXX0X):
#ifdef __GXX_EXPERIMENTAL_CXX0X__
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// DR 130. Associative erase should return an iterator.
/**
* #brief Erases an element from a %map.
* #param position An iterator pointing to the element to be erased.
* #return An iterator pointing to the element immediately following
* #a position prior to the element being erased. If no such
* element exists, end() is returned.
*
* This function erases an element, pointed to by the given
* iterator, from a %map. Note that this function only erases
* the element, and that if the element is itself a pointer,
* the pointed-to memory is not touched in any way. Managing
* the pointer is the user's responsibility.
*/
iterator
erase(iterator __position)
{ return _M_t.erase(__position); }
#else
/**
* #brief Erases an element from a %map.
* #param position An iterator pointing to the element to be erased.
*
* This function erases an element, pointed to by the given
* iterator, from a %map. Note that this function only erases
* the element, and that if the element is itself a pointer,
* the pointed-to memory is not touched in any way. Managing
* the pointer is the user's responsibility.
*/
void
erase(iterator __position)
{ _M_t.erase(__position); }
#endif
Example with old and new style:
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
typedef map<int, int> t_myMap;
typedef vector<t_myMap::key_type> t_myVec;
int main() {
cout << "main() ENTRY" << endl;
t_myMap mi;
mi.insert(t_myMap::value_type(1,1));
mi.insert(t_myMap::value_type(2,1));
mi.insert(t_myMap::value_type(3,1));
mi.insert(t_myMap::value_type(4,1));
mi.insert(t_myMap::value_type(5,1));
mi.insert(t_myMap::value_type(6,1));
cout << "Init" << endl;
for(t_myMap::const_iterator i = mi.begin(); i != mi.end(); i++)
cout << '\t' << i->first << '-' << i->second << endl;
t_myVec markedForDeath;
for (t_myMap::const_iterator it = mi.begin(); it != mi.end() ; it++)
if (it->first > 2 && it->first < 5)
markedForDeath.push_back(it->first);
for(size_t i = 0; i < markedForDeath.size(); i++)
// old erase, returns void...
mi.erase(markedForDeath[i]);
cout << "after old style erase of 3 & 4.." << endl;
for(t_myMap::const_iterator i = mi.begin(); i != mi.end(); i++)
cout << '\t' << i->first << '-' << i->second << endl;
for (auto it = mi.begin(); it != mi.end(); ) {
if (it->first == 5)
// new erase() that returns iter..
it = mi.erase(it);
else
++it;
}
cout << "after new style erase of 5" << endl;
// new cend/cbegin and lambda..
for_each(mi.cbegin(), mi.cend(), [](t_myMap::const_reference it){cout << '\t' << it.first << '-' << it.second << endl;});
return 0;
}
prints:
main() ENTRY
Init
1-1
2-1
3-1
4-1
5-1
6-1
after old style erase of 3 & 4..
1-1
2-1
5-1
6-1
after new style erase of 5
1-1
2-1
6-1
Process returned 0 (0x0) execution time : 0.021 s
Press any key to continue.

Pretty sad, eh? The way I usually do it is build up a container of iterators instead of deleting during traversal. Then loop through the container and use map.erase()
std::map<K,V> map;
std::list< std::map<K,V>::iterator > iteratorList;
for(auto i : map ){
if ( needs_removing(i)){
iteratorList.push_back(i);
}
}
for(auto i : iteratorList){
map.erase(*i)
}

Related

How to get a std::list<T>::iterator from an element of that list?

Given a std:: list
std::list< int > myList
and a reference (or a pointer) to an element in that list
int& myElement | int* pElement
So, basically I know the address of that element
How can I get an std::list<int>::iterator to that element efficiently?
A slow yet working example is
const_iterator it
for( it = myList.begin(); it != &myElement; ++it)
{
// do nothing, for loop terminates if "it" points to "myElem"
}
Is there a quicker way? like
const_iterator it = magicToIteratorConverter( myList, myElem )
Case of vector (but I need list):
For a vector, you could do the following:
const int* pStart = &myVector[0] // address of first element
const int* pElement = &myElem; // address of my element
const idx = static_cast< int >( pElement- pStart ); // no need to divide by size of an elem
std::vector< int >::iterator it = myVector.begin() + idx;
Case of std::list:
a std::list<int>::iterator is not an int*, you need to access the element in the iterator and get its address. Also, std::find_if takes care of most of the boilerplate for you.
auto iter = std:find_if(myList.begin(), myList.end(),
[&myElement](const int & listElement)
{ return &myElement == &listElement; });
Writing the loop out yourself would look like:
auto iter = myList.end();
for(auto i = myList.begin(); i != myList.end(); ++i)
if(&*i == &myElement)
{
iter = i;
break;
}
This is actually a good question and IMHO a shame that there is no std way to do what OP has asked. If you understand that a list node looks essentially like this
struct list_node {
list_node* prev;
list_node* next;
T yourType;
}
it is bad that there is no default way to get to the node (the iterator is a pointer to the node) if you have a pointer to yourType without searching through the whole container.
Since std is not helping you have to get your hands dirty:
#include <list>
#include <iostream>
//This is essentially what you are looking for:
std::list<int>::iterator pointerToIter (int* myPointer) {
//Calculates the distance in bytes from an iterator itself
//to the actual type that is stored at the position the
//iterator is pointing to.
size_t iterOffset = (size_t)&(*((std::list<void*>::iterator)nullptr));
//Subtract the offset from the passed pointer and make an
//iterator out of it
std::list<int>::iterator iter;
*(intptr_t*)&iter = (intptr_t)myPointer - iterOffset;
//You are done
return iter;
}
int main () {
std::list<int> intList;
intList.push_back (10);
int* i1 = &intList.back ();
intList.push_back (20);
intList.push_back (30);
int* i3 = &intList.back ();
intList.push_back (40);
intList.push_back (50);
int* i5 = &intList.back ();
std::cout << "Size: " << intList.size () << " | Content: ";
for (const int& value : intList)
std::cout << value << " ";
std::cout << std::endl;
intList.erase (pointerToIter (i1));
intList.erase (pointerToIter (i3));
intList.erase (pointerToIter (i5));
std::cout << "Size: " << intList.size () << " | Content: ";
for (const int& value : intList)
std::cout << value << " ";
std::cout << std::endl;
return 0;
}
Output (to prove that it works as intended):
Size: 5 | Content: 10 20 30 40 50
Size: 2 | Content: 20 40
This works perfectly even if an implementation of std::list would use a different layout for the list-node or add some more members to it. I also included the assembler code generated to see that the function is essentially reduced to myPointer - 0x10 (0x10 = 16 is the size of 2 pointers on a 64bit machine).
Assembler (with at least -O1):
std::list<int>::iterator pointerToIter (int* myPointer) {
0: 48 8d 47 f0 lea rax,[rdi-0x10]
}
4: c3 ret
Given a list, you can only get to anything by starting from one end walking through it, making sure you don't go beyond the end.
const_iterator it, end;
for( it = myList.begin(), end = myList.end(); it!=end && it != &myElement; ++it)
{
// do nothing, for loop terminates if "it" points to "myElem"
// or if we don't find your element.
}
Of course, you could use a standard algorithm, like std::find to look for it.
Alternatively, you could keep hold of the iterator when you insert, and under many conditions it will still be valid later on.
If you want lookup speed, you should probably use something other than a list.
If you have something like
int x = 42;
int * this_might_be_handy = &x;
myList.insert(x);
myList now has a COPY of the number - it has the value 42, but in a different memory location.
If you kept pointers to ints in the list, that would be different. getting the value from the front of the list and looking at the address will not give the same address as x.
But you would have to manage them smartly.
This is a dissected, formalized (C++11) and commented version of the very interesting solution of Xatian above. That solution is O(1) (that is one of the reasons it is so interesting), but it assumes some things that should be taken into account (Xatian's deep knowledge of the STL implementation of lists is the other reason why it is so interesting).
#include <iostream>
#include <list>
#include <cstdint>
int main(void)
{
// ASSUMPTIONS:
// 1.- nullptr == 0
// 2.- A std::list iterator is an object that contains just one thing: a pointer to the body of the iterator.
// 3.- A std::list iterator has a constructor that admits a pointer to a body provided by the user, which creates the iterator with that (assumed) existing body.
using DataType = int;
using DataList = std::list<DataType>;
std::cout << "Nullptr= " << reinterpret_cast<size_t>(nullptr) << std::endl;
std::cout << "Size of a pointer = " << sizeof(nullptr) << ", size of iterator = " << sizeof(DataList::iterator) << std::endl;
static_assert(reinterpret_cast<size_t>(nullptr) == 0,
"In this compiler, nullptr is not 0 and this will not work");
// we have a list filled with something.
DataList mylist{1,2,3,4};
// and an iterator pointing to some data.
DataList::iterator itaux{mylist.begin()};
++itaux;
++itaux;
// 1. calculate the offset of the data in a list iterator w.r.t. the beginning of the iterator body
DataList::iterator it{nullptr}; // call the iterator constructor. Nullptr becomes the address of the body where the iterator would store prev/next/data
// since nullptr is assumed to be 0, this is the same as to declare an iterator with its body at 0x00000000
DataType & itp = *it; // this is a reference to the user's data in the iterator body
// that iterator is a fake and does not contain any data, but since we are only dealing with addresses, no problem...
DataType * aitp = & itp; // this gets the address equivalent to the reference, which is at some point in memory from 0
size_t iteroffset = reinterpret_cast<size_t>(aitp); // That address becomes, actually, the offset of the data w.r.t. the beginning of the iterator body
std::cout << "Offset from iterator body start to data = " << iteroffset << std::endl;
// 2. we can get the pointer to the data from our existing iterator
DataType * mypointer = &(*itaux);
// 3. we can create a valid iterator from the pointer to the data
DataList::iterator iter;
*(reinterpret_cast<intptr_t*>(&iter)) = reinterpret_cast<intptr_t>(mypointer) - iteroffset; // the address of the beginning of the body (mypointer-iteroffset) is stored into
// the pointer to the body that the iterator actually is
std::cout << "pointed element: " << (*iter) << std::endl;
return(0);
}

Decrementing std::vector::iterator before std::vector::begin()

Following one of the "deleting while iterating" patterns on a vector, I don't understand why this code works, or if it's making use of undefined behavior:
The Code:
#include <vector>
#include <iostream>
int main(int argc, char* argv[], char* envz[])
{
std::vector<std::string> myVec;
myVec.push_back("1");
myVec.push_back("2");
myVec.push_back("3");
for (std::vector<std::string>::iterator i = myVec.begin();
i != myVec.end();
++i)
{
if ("1" == *i)
{
std::cout << "Erasing " << *i << std::endl;
i = myVec.erase(i);
--i;
continue;
}
std::cout << *i << std::endl;
}
return 0;
}
The Output:
>g++ -g main.cpp
>./a.out
Erasing 1
2
3
Question:
Consider the first iteration of the for-loop:
i is myVec.begin(), which "points to" 1.
We enter the conditional block.
1 is erased and i is set to one past the erased element, i.e. 2, which is now also pointed to by myVec.begin()
I decrement i, so now it points to...one prior to myVec.begin() ???
I'm confused by why this seems to work, as evidenced by the output, but something feels fishy about decrementing the iterator. This code is easy enough to rationalize if the conditional is if ("2" == *i), because the iterator decrement still places it at a valid entry in the vector. I.e. if we conditionally erased 2, i would be set to point to 3, but then manually decremented and thus point to 1, followed by the for-loop increment, setting it to point back to 3 again. Conditionally erasing the last element is likewise easy to follow.
What Else I Tried:
This observation made me hypothesize that decrementing prior to vector::begin() was idempotent, so I tried addition an additional decrement, like so:
#include <vector>
#include <iostream>
int main(int argc, char* argv[], char* envz[])
{
std::vector<std::string> myVec;
myVec.push_back("1");
myVec.push_back("2");
myVec.push_back("3");
for (std::vector<std::string>::iterator i = myVec.begin();
i != myVec.end();
++i)
{
if ("1" == *i)
{
std::cout << "Erasing " << *i << std::endl;
i = myVec.erase(i);
--i;
--i; /*** I thought this would be idempotent ***/
continue;
}
std::cout << *i << std::endl;
}
return 0;
}
But this resulted in a segfault:
Erasing 1
Segmentation fault (core dumped)
Can someone explain why the first code bock works, and specifically why the single decrement after erasing the first element is valid?
No, your code has undefined behaviour: if i == myVec.begin(), then i = myVec.erase(i); results in i again being (the new value of) myVec.begin(), and --i has undefined behaviour since it goes outside the valid range for the iterator.
If you don't want to use the erase-remove idiom (i.e. myVec.erase(std::remove(myVec.begin(), myVec.end(), "1"), myVec.end())), then the manual loop-while-mutating looks like this:
for (auto it = myVec.begin(); it != myVec.end(); /* no increment! */) {
if (*it == "1") {
it = myVec.erase(it);
} else {
++it;
}
}
Regardless, the crucial point both here and in your original code is that erase invalidates iterators, and thus the iterator must be re-assigned with a valid value after the erasing. We achieve this thanks to the return value of erase, which is precisely that new, valid iterator that we need.
This might work in some compilers, but might fail in others (e.g. the compiler might actually check in runtime that you are not decrementing under begin() and throw exception in such case - I believe that at least one compiler does it but don't remember which one).
In this case the general pattern is to not increment in the for but inside the loop:
for (std::vector<std::string>::iterator i = myVec.begin();
i != myVec.end();
/* no increment here */)
{
if ("1" == *i)
{
std::cout << "Erasing " << *i << std::endl;
i = myVec.erase(i);
continue;
}
std::cout << *i << std::endl;
++i;
}
With vector the wrong iteration might actually work in more cases, but you'd have very bad time if you try that e.g. with std::map or std::set.
The key here is the continue right after decrementing.
By calling it, ++i will be triggered by the loop iteration before dereferencing i.

Can you safely use a std::set as the key to a std::map? [duplicate]

How do I remove from a map while iterating it? like:
std::map<K, V> map;
for(auto i : map)
if(needs_removing(i))
// remove it from the map
If I use map.erase it will invalidate the iterators
The standard associative-container erase idiom:
for (auto it = m.cbegin(); it != m.cend() /* not hoisted */; /* no increment */)
{
if (must_delete)
{
m.erase(it++); // or "it = m.erase(it)" since C++11
}
else
{
++it;
}
}
Note that we really want an ordinary for loop here, since we are modifying the container itself. The range-based loop should be strictly reserved for situations where we only care about the elements. The syntax for the RBFL makes this clear by not even exposing the container inside the loop body.
Edit. Pre-C++11, you could not erase const-iterators. There you would have to say:
for (std::map<K,V>::iterator it = m.begin(); it != m.end(); ) { /* ... */ }
Erasing an element from a container is not at odds with constness of the element. By analogy, it has always been perfectly legitimate to delete p where p is a pointer-to-constant. Constness does not constrain lifetime; const values in C++ can still stop existing.
I personally prefer this pattern which is slightly clearer and simpler, at the expense of an extra variable:
for (auto it = m.cbegin(), next_it = it; it != m.cend(); it = next_it)
{
++next_it;
if (must_delete)
{
m.erase(it);
}
}
Advantages of this approach:
the for loop incrementor makes sense as an incrementor;
the erase operation is a simple erase, rather than being mixed in with increment logic;
after the first line of the loop body, the meaning of it and next_it remain fixed throughout the iteration, allowing you to easily add additional statements referring to them without headscratching over whether they will work as intended (except of course that you cannot use it after erasing it).
Assuming C++11, here is a one-liner loop body, if this is consistent with your programming style:
using Map = std::map<K,V>;
Map map;
// Erase members that satisfy needs_removing(itr)
for (Map::const_iterator itr = map.cbegin() ; itr != map.cend() ; )
itr = needs_removing(itr) ? map.erase(itr) : std::next(itr);
A couple of other minor style changes:
Show declared type (Map::const_iterator) when possible/convenient, over using auto.
Use using for template types, to make ancillary types (Map::const_iterator) easier to read/maintain.
The C++20 draft contains the convenience function std::erase_if.
So you can use that function to do it as a one-liner.
std::map<K, V> map_obj;
//calls needs_removing for each element and erases it, if true was reuturned
std::erase_if(map_obj,needs_removing);
//if you need to pass only part of the key/value pair
std::erase_if(map_obj,[](auto& kv){return needs_removing(kv.first);});
In short "How do I remove from a map while iterating it?"
With old map impl: You can't
With new map impl: almost as #KerrekSB suggested. But there are some syntax issues in what he posted.
From GCC map impl (note GXX_EXPERIMENTAL_CXX0X):
#ifdef __GXX_EXPERIMENTAL_CXX0X__
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// DR 130. Associative erase should return an iterator.
/**
* #brief Erases an element from a %map.
* #param position An iterator pointing to the element to be erased.
* #return An iterator pointing to the element immediately following
* #a position prior to the element being erased. If no such
* element exists, end() is returned.
*
* This function erases an element, pointed to by the given
* iterator, from a %map. Note that this function only erases
* the element, and that if the element is itself a pointer,
* the pointed-to memory is not touched in any way. Managing
* the pointer is the user's responsibility.
*/
iterator
erase(iterator __position)
{ return _M_t.erase(__position); }
#else
/**
* #brief Erases an element from a %map.
* #param position An iterator pointing to the element to be erased.
*
* This function erases an element, pointed to by the given
* iterator, from a %map. Note that this function only erases
* the element, and that if the element is itself a pointer,
* the pointed-to memory is not touched in any way. Managing
* the pointer is the user's responsibility.
*/
void
erase(iterator __position)
{ _M_t.erase(__position); }
#endif
Example with old and new style:
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
typedef map<int, int> t_myMap;
typedef vector<t_myMap::key_type> t_myVec;
int main() {
cout << "main() ENTRY" << endl;
t_myMap mi;
mi.insert(t_myMap::value_type(1,1));
mi.insert(t_myMap::value_type(2,1));
mi.insert(t_myMap::value_type(3,1));
mi.insert(t_myMap::value_type(4,1));
mi.insert(t_myMap::value_type(5,1));
mi.insert(t_myMap::value_type(6,1));
cout << "Init" << endl;
for(t_myMap::const_iterator i = mi.begin(); i != mi.end(); i++)
cout << '\t' << i->first << '-' << i->second << endl;
t_myVec markedForDeath;
for (t_myMap::const_iterator it = mi.begin(); it != mi.end() ; it++)
if (it->first > 2 && it->first < 5)
markedForDeath.push_back(it->first);
for(size_t i = 0; i < markedForDeath.size(); i++)
// old erase, returns void...
mi.erase(markedForDeath[i]);
cout << "after old style erase of 3 & 4.." << endl;
for(t_myMap::const_iterator i = mi.begin(); i != mi.end(); i++)
cout << '\t' << i->first << '-' << i->second << endl;
for (auto it = mi.begin(); it != mi.end(); ) {
if (it->first == 5)
// new erase() that returns iter..
it = mi.erase(it);
else
++it;
}
cout << "after new style erase of 5" << endl;
// new cend/cbegin and lambda..
for_each(mi.cbegin(), mi.cend(), [](t_myMap::const_reference it){cout << '\t' << it.first << '-' << it.second << endl;});
return 0;
}
prints:
main() ENTRY
Init
1-1
2-1
3-1
4-1
5-1
6-1
after old style erase of 3 & 4..
1-1
2-1
5-1
6-1
after new style erase of 5
1-1
2-1
6-1
Process returned 0 (0x0) execution time : 0.021 s
Press any key to continue.
Pretty sad, eh? The way I usually do it is build up a container of iterators instead of deleting during traversal. Then loop through the container and use map.erase()
std::map<K,V> map;
std::list< std::map<K,V>::iterator > iteratorList;
for(auto i : map ){
if ( needs_removing(i)){
iteratorList.push_back(i);
}
}
for(auto i : iteratorList){
map.erase(*i)
}

Moving object to front of vector c++

I have a vector<Suggestions> finalSuggestions that contains a string word and some int num.
If this word meets some condition, I want to move that object to the front of the vector, removing it from wherever it was.
I am able to insert to the beginning of the list with vector::insert
for (auto &x: finalSuggestions) {
if ( double((x.num)/(topword.num)) < 50)
{
finalSuggestions.insert(finalSuggestions.begin(),x);
break;
}
}
But I do not know how to remove it from where it is in the list.
For example, for some arbitrary vector { 1,2,3,4,50,6,7,8,9 },
if 50 meets the criteria, move it to the front of the list and delete it from where it was, returning { 50,1,2,3,4,6,7,8,9 }. The code above returns { 50,1,2,3,4,50,6,7,8,9 }
I was looking into vector::erase, but I'm having problems, and its taking longer than it should.
I envision a simple solution (but this obviously doesn't work)
for (auto &x: finalSuggestions) {
if ( double((x.num)/(topword.num)) < 50)
{
finalSuggestions.insert(finalSuggestions.begin(),x);
finalSuggestions.erase(x);
break;
}
}
I read up on the erase-remove idiom (here is my implementation):
finalSuggestions.erase( remove( begin(finalSuggestions), end(finalSuggestions), x ), end(finalSuggestions) );
but am getting an error that I don't understand:
In instantiation of '_FIter std::remove(_FIter, _FIter, const _Tp&) [with _FIter = __gnu_cxx::__normal_iterator<Suggestion*, std::vector<Suggestion> >; _Tp = Suggestion]':|
Use std::rotate. It's a lot faster than deleting and reinserting.
Eg:
for (auto it = finalSuggestions.begin(), lim = finalSuggestions.end();
it != lim;
++it) {
if (it->num < 50 * topword.num) {
std::rotate(finalSuggestions.begin(), it, it + 1);
break;
}
}
Even better, as #JerryCoffin suggests in a comment, use std::find_if to find the pivot:
auto pivot = std::find_if(finalSuggestions.begin(),
finalSuggestions.end(),
[&topword](const Suggestions& s) -> bool {
return s.num < 50 * topword.num;
});
if (pivot != finalSuggestions.end()) {
std::rotate(finalSuggestions.begin(), pivot, pivot + 1);
}
For vector::erase you need an iterator, so range-based for can't be used. Use simple for loop instead. First erase an element, and then insert it, because insert invalidates iterators:
for (auto it = finalSuggestions.begin(); it != finalSuggestions.end(); ++it) {
if (some_condition(*it)) {
auto x = *it; // or std::move(*it)
finalSuggestions.erase(it);
finalSuggestions.insert(finalSuggestions.begin(), x /* or std::move(x) */);
break;
}
}
Using std::move will allow you to move an element around instead of copying it, which may save you some cycles.
Your iterator makes it difficult to know the position of the element in question. You might want to try using a standard for iterator which allows access to the position (used by std::vector::erase)
int len=finalSuggestions.size();
for (int i=0, ; i<len; ++i) {
// Save a copy of the item
auto item = finalSuggestions.at(i);
if (double((item.num)/(topword.num)) < 50) {
// Erase the item in the list
finalSuggestions.erase(i);
// Add the copy of the item back in at the front
finalSuggestions.insert(finalSuggestions.begin(), item);
break;
}
}
... or using a std::iterator ...
for (auto it = finalSuggestions.begin(); it != finalSuggestions.end(); ++it) {
if (double((*it->num)/(topword.num)) < 50) {
// Save a copy of the item
auto item = *it;
// Erase the item in the list
finalSuggestions.erase(it);
// Add the copy of the item back in at the front
finalSuggestions.insert(finalSuggestions.begin(), item);
break;
}
}
std::vector objects use contiguous memory for their elements, which means actually moving memory around during altering of the container. If you are going to be moving elements around you may want to look into std::list or std:deque. The definition of these containers are nearly identical (read: drop in replacements) to each other making it fairly straight-forward to replace them.
Suggestion:
The std::deque is designed for optimal insertions at both the beginning and the end of the container. Taken from the site cplusplus.com:
... they provide a functionality similar to vectors, but with efficient insertion and deletion of elements also at the beginning of the sequence, and not only at its end. But, unlike vectors, deques are not guaranteed to store all its elements in contiguous storage locations: ...
Anton's answer is correct. However if you do this sort of thing a lot you should consider a different data structure. Both the erase and the insert are O(N) operations, where N is the size of the vector. A list would be better if this is a common operation.
It is functionally equivalent to Anton's answer, but I would use std::find_if to get the an iterator to the element you are looking for instead of a loop.
//add #include <algorithm> to your source file
auto result = std::find_if(finalSuggestions.begin(), finalSuggestions.end(), condition_func);
if(result != finalSuggestions.end())
{
auto resultValue = *result;
finalSuggestions.erase(result);
finalSuggestions.insert(finalSuggestions.begin(), resultValue);
}
condition_func should be a function returning bool that takes a parameter matching the type of the elements in your vector (in this case, Suggestion):
bool condition_func(Suggestion elementValue) { /*condition here*/ }
More information on find_if is available here.
Maybe using std::iter_swap could solve your problem.
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main () {
vector<int> myvector{};
for(int io{}; io<7; ++io) myvector.push_back(io+1);
for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
cout << ' ' << *it;
cout << '\n';
iter_swap(myvector.begin(),myvector.begin()+2);//exchange the third element with the first.
cout << "myvector contains:";
for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}

Removing entries of particular key in C++ STL multimap

I have this sample code to insert entries to a multimap. I am trying to delete particular entries of a specified key. But this code goes into infinite loop. Can someone help me with this code?
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
multimap<string, string> names;
string n;
names.insert(pair<string, string>("Z", "F"));
names.insert(pair<string, string>("Z", "A"));
names.insert(pair<string, string>("S", "T"));
names.insert(pair<string, string>("S", "A"));
names.insert(pair<string, string>("S", "J"));
names.insert(pair<string, string>("D", "H"));
names.insert(pair<string, string>("D", "W"));
names.insert(pair<string, string>("D", "R"));
multimap<string, string>::iterator p;
p = names.find("Z");
if(p != names.end()) { // found a name
do {
cout << n << ", " << p->second;
cout << endl;
if (p->second.compare("A") == 0) {
names.erase(p);
p++;
} else {
p++;
}
} while (p != names.upper_bound("Z"));
}
else{
cout << "Name not found.\n";
}
p = names.find("Z");
if(p != names.end()) { // found a name
do {
cout << n << ", " << p->second;
cout << endl;
} while (p != names.upper_bound("Z"));
}
else{
cout << "Name not found.\n";
}
return 0;
}
In the above I am looking up using Key value "Z" and want to delete "A".
multimap::erase invalidates any iterators to the erase elements, so the lines
names.erase(p);
p++;
erases p, thus invalidating it, and then attempt to increment an invalid iterator. You can fix this by copying p to a temporary, incrementing p, and then erasing the temporary iterator.
multimap<string, string>::iterator temp = p;
++p;
names.erase(temp);
Alternatively if you're using C++11 then multimap::erase returns the next iterator in the container
p = names.erase(p);
Edit: the above isn't actually the source of your infinite loop. In the second loop you don't increment p, so it goes forever. However it is still something you should fix as it can cause unpredictable and difficult to track down bugs.
As said by others, advancing an iterator that points to an element that was just erased is not guaranteed to work. What you can do instead is to use the postfix ++ operator to retrieve an iterator to the element that followed the erased one before it was erased:
names.erase(p++);
In C++11, you can alternatively retrieve the return value of erase, which points to the following element (or is end() if there is no more element):
p = names.erase(p);
It has also been said already that your second loop is an infinite loop by definition because it never increments the counter.
However, there is one more thing that should be said: Your method of checking if the last element in a range of elements has been reached is not very efficient: You call upper_bound in every iteration of the loop, which will cause a new O(log(n)) tree search each time, although the iterator returned will always be the same.
You can obviously improve this by running upper_bound before you enter the loop and store the result. But even better, I'd suggest your run the equal_range function once, and then simply iterate through the range it returned:
typedef multimap<string,string>::const_iterator mapit;
std::pair<mapit,mapit> range = names.equal_range("Z");
mapit it = range.first;
while (it != range.second)
if (it->second == "A")
names.erase(it++);
else
++it;
In C++11, the use of auto will make this look even better.