ALL,
std::vector<string>::iterator it;
string orig;
bool found = false;
for( it = vec.begin(); it < vec.end() && !found; it++ )
{
if( ... )
{
found = true;
orig = (*it);
}
}
After I get out of the loop the iterator become invalid even if I have found = true.
How do I keep the iterator? I need it for later processing..
MSVC 2017, Windows 8.1
TIA!!!
You could decrement it in the case you found it, to undo the final it++ that you don't want.
if (found) it--;
Or you could use std::find_if, where ... uses value instead of *it.
auto it = std::find_if(vec.begin(), vec.end(), [](std::string & value) ( return value.find("abc"); });
auto found = it != vec.end();
auto orig = *it;
Related
I tried to follow the erase example for sample iterator over list but I can't make it work.
Here is the code so far:
for (list<list<string>>::iterator itr = listOfList.begin(), ; itr != listeOfListe.end(); itr++){
if (condition) {
for (list<string>::iterator it6 = itr->begin(); it6 != itr->end(); it6++)
{
itr.erase(*it6);
}
}
}
I get the following error:
class "std::_List_iterator<std::__cxx11::list<std::string, std::allocator<std::string>>>" has no member "erase"
Which indicates that it considers that itr is a list of list of string, why isn't itr simply a list of string since it iterates over listOfList?
You are trying to call erase() on an iterator itself, not on the list that the iterator refers to. That is why you are getting the compiler error. You need to dereference the iterator via operator* or operator-> to access the list to call erase() on.
Also, your inner loop is not accounting for the fact that list::erase() invalidates the specified iterator, so your use of it6++ is undefined behavior after calling erase(it6). You need to use the iterator that erase() returns in order to continue the loop correctly, eg: it6 = erase(it6).
Try something more like this instead:
// pre C++11
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
if (condition) {
list<string>::iterator it6 = itr->begin();
while (it6 != itr->end()) {
it6 = itr->erase(it6);
}
}
}
// C++11 and later
for (auto &listOfStrings : listOfList){
if (condition) {
auto it6 = listOfStrings.begin();
while (it6 != listOfStrings.end()) {
it6 = listOfStrings.erase(it6);
}
}
}
Which simplifies to this:
// pre C++11
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
if (condition) {
itr->clear();
}
}
// C++11 and later
for (auto &listOfStrings : listOfList){
if (condition) {
listOfStrings.clear();
}
}
Alternatively:
// pre C++11
if (condition) {
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
itr->clear();
}
}
// C++11 and later
if (condition) {
for (auto &listOfStrings : listOfList){
listOfStrings.clear();
}
}
I don't think that is what you are looking for, though. Your title says you want to "remove a string from a list", so you are probably looking for something more like this instead:
// pre C++11
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
if (condition) {
itr->remove(string); // removes all matching strings
}
}
// C++11 and later
for (auto &listOfStrings : listOfList){
if (condition) {
listOfStrings.remove(string); // removes all matching strings
}
}
Alternatively:
// pre C++11
if (condition) {
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
itr->remove(string); // removes all matching strings
}
}
// C++11 or later
if (condition) {
for (auto &listOfStrings : listOfList){
listOfStrings.remove(string); // removes all matching strings
}
}
Or, maybe you are looking for something more like this instead?
// pre C++11
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
list<string>::iterator it6 = itr->begin();
while (it6 != itr->end()) {
if (condition(*it6)) {
it6 = itr->erase(it6);
} else {
++it6;
}
}
}
// C++11 and later
for (auto &listOfStrings : listOfList){
auto it6 = listOfStrings.begin();
while (it6 != itr->end()) {
if (condition(*it6)) {
it6 = listOfStrings.erase(it6);
} else {
++it6;
}
}
}
Which, in that latter case, simplifies to this in C++20:
for (auto &listOfStrings : listOfList){
std::erase_if(listOfStrings, [](string &s){ return condition(s); });
}
Please note that behavior of this program is undefined.
for (list<string>::iterator it6 = itr->begin(); it6 != itr->end(); it6++) {
itr->erase(it6); // !!
}
Code marked with // invalidates iterator it6. Incrementing it in the loop is now illegal, since only valid list iterators can be incremented.
To correct the snippet, use following:
for (list<string>::iterator it6 = itr->begin(); it6 != itr->end(); ) {
it6 = itr->erase(it6);
}
In this example, we use the fact that std::list::erase returns the iterator to the next element after the one erased.
However, this is only for illustration, assuming you would want to learn how to work with iterators. Better code would simply be
itr->clear();
template<typename C> bool binary_search(list<C>& a, C val) {
typename list <C>::iterator it;
typename list <C>::iterator it2;
typename list <C>::iterator it3;
size_t elements = a.size();
for (it = a.begin(), it3 = a.end(); it != it3; ) {
//find middle element
elements = elements / 2;
for (it2 = it; it2 < elements; it2++) {
}
if (*it2 < val)
*it = *it2;
else if (*it2 > val)
*it3 = *it2;
else if (*it2 = val)
return true;
else
return false;
}
}
Hello! I am having trouble implementing this search function.
val in the parameters is the value we are searching for.
when I try to compile the code I get
error C2676: binary '<': 'std::_List_iterator<std::_List_val<std::_List_simple_types<_Ty>>>'
does not define this operator or a conversion to a type acceptable to the predefined operator
This error occurs at:
for (it2 = it; it2 < elements; it2++)
also is there a better way to find the middle element?
elements holds the index of the last element you want to check.
it2 is an iterator. operator< is not defined for those types. You can use it and elements to create an iterator to make comparisons possible.
size_t elements = a.size();
for (it = a.begin(), it3 = a.end(); it != it3; ) {
//find middle element
elements = elements / 2;
auto end = std::next(it, elements); // added iterator
for (it2 = it; it2 != end; it2++) { // use added iterator
}
if (*it2 < val)
*it = *it2;
else if (*it2 > val)
*it3 = *it2;
else if (*it2 = val) // note: this assigns val to *it2, probably a bug
return true;
else
return false;
}
Pretty sure this is not a good way to solve this lol, but this is my final version and works for my current assignment.
template<typename C> bool binary_search(list<C>& a, C val) {
typename list <C>::iterator it;
typename list <C>::iterator it2;
typename list <C>::iterator it3 = a.end();
size_t elements = a.size();
size_t i = 0;
for (it = a.begin(), it3--; it != it3; ) {
//find middle element
elements = elements / 2;
for (it2 = it, i = 0; i < elements; it2++, i++) {
}
if (*it2 < val)
it = it2;
else if (*it2 > val)
it3 = it2;
else if (*it2 = val)
return true;
else
return false;
}
}
Thanks to fas for pointing out what was wrong!
To move an iterator by a number of places you can use std::advance:
elements = elements / 2;
it2 = it;
std::advance(it2, elements);
Note however that performing a binary search on a std::list is unlikely to be efficient unless you have a very expensive comparison function. If you want random access you should use std::vector instead. std::vector's iterators also allow you to just add numbers to them without using std::advance:
elements = elements / 2;
it2 = it + elements;
I assume you are implementing binary search as an exercise but if not note that the standard library does have a binary search function it just has a non obvious name: std::upper_bound std::lower_bound
I'm trying to remove elements that have the same key and value in a multimap. This is my code for now. After deleting the element, I get exception.
multimap<string, CStudent> m_StudentMap;
void removeDuplicates() {
for (auto it1 = m_StudentMap.begin(); it1 != --m_StudentMap.end(); it1++) {
for (auto it2 = next(it1, 1); it2 != m_StudentMap.end(); it2++) {
if (it1->first == it2->first) {
if (it1->second == it2->second) {
m_StudentMap.erase(it2);
}
}
}
}
}
You were nearly right, but the trick with erasing elements in maps while iterating is to capture the new iterator returned by erase. I've also generalised the function so it can be used on an argument rather than being limited to m_StudentMap, and stopped the inner loop as soon as the keys diverge.
template <typename K, typename V>
void removeDuplicates(std::multimap<K, V>& mmap)
{
if (mmap.size() < 2) return;
for (auto it = mmap.begin(); it != prev(mmap.end()); ++it)
for (auto it2 = next(it); it2 != mmap.end() && it2->first == it->first; )
if (it->second == it2->second)
it2 = mmap.erase(it2);
else
++it2;
}
You can see it run / fork it etc. here.
How to iterate through a std::map<string,int> and std::vector<int> using single for loop ?
I have seen this questions but could not solve my problem.
I am trying like this
map<string,int> data;
data["Shravan"] = 1;
data["Mama"] = 2;
data["Sa1"] = 3;
data["Jhandu"] = 4;
vector<int> values = {1,2,3,4};
for(const auto& it1: data,it2 : values) {
// Do something
}
Edit : I can not go through one by one. Because i am using the key of std::map and value of std::vector in the same function. Which will be called inside for loop.
Both the container of same size.
If you know there's both the same length, use something like:
auto vit = begin(value);
auto mit = begin(data);
for (; vit != end(value); ++mit, ++vit) {
// Use mit and vit
}
How about a do-while? Provided that your containers aren't empty.
auto iv = std::begin(value);
auto id = std::begin(data);
do {
// Use those iterators
} while(++iv != std::end(value) && ++id != std::end(data))
Or use while if you'd like to handle empty containers too.
auto iv = std::begin(value);
auto id = std::begin(data);
while(iv != std::end(value) && id != std::end(data)) {
// Use those iterators
iv++; id++;
}
Consider boost::zip_iterator discussed in this answer https://stackoverflow.com/a/8513803/2210478
You can iterate over both the map and the vector and make sure to check the iterators against the end of the corresponding container.
auto map_iter = data.begin();
auto vec_iter = value.begin();
for (; map_iter != data.end() && vec_iter != value.end();
++map_iter, ++vec_iter) {
// Use map_iter and vec_iter
}
for (Shape *i : shapes) {
for (Shape *j : shapes) {
if (i != j) {
if (check(i,j)){
shapes.erase(remove(shapes.begin(), shapes.end(), i), shapes.end());
this causes an error because it's going to carry on iterating even though i does not exist, my question is how do I cleanly do this? currently I get an error "vector iterator not incrementable"
Can i just exit the second loop and continue in the first one?
You cannot erase elements from a vector when you are iterating it by for range loop, as internally it uses iterators that would be invalidated. This should work:
auto end = shapes.end();
for( auto it = shapes.begin(); it != end; ++it ) {
end = shapes.erase( std::remove_if( std::next( it ), shapes.end(), [it]( Shape *s ) {
return check( *it, s );
}, shapes.end() );
}
Note this code is slightly more effective than yours but it assumes that check( s1, s2 ) == check( s2, s1 ), if you can change your check() function to do strict ordering comparison, rather than equivalence, then you would be able to use std::sort and std::unique which are even more effective.
You can't modify the positioning of your shapes elements while using ranged-based for loops. The range loop uses iterators internally, and erasing vector elements invalidates existing iterators.
Try something more like this instead:
auto iter = shapes.begin();
auto end = shapes.end();
while (iter != end) {
auto iter2 = shapes.begin();
bool erased = false;
while (iter2 != end) {
if ((iter != iter2) && check(*iter, *iter2)) {
iter = shapes.erase(iter);
end = shapes.end();
erased = true;
break;
}
++iter2;
}
if (!erased)
++iter;
}
Alternatively, maybe something more like this would also work:
shapes.erase(
std::remove_if(shapes.begin(), shapes.end(),
[shapes&](Shape *i) {
for (Shape *j : shapes) {
if ((i != j) && check(i, j)) {
return true;
}
}
return false;
}
),
shapes.end()
);
You cannot use a range-for loop in this case. Instead use a standard loop with iterators:
for (auto iter = shapes.begin(); iter != shapes.end(); iter++)