Loop for container class c++ - c++

I have this loop
for(int i=0;i<vec1.size();++i)
{
if(vec1[i]==*p)
{
vec1[i]=*p;
cout<<"element updated"<<endl;
}
else
{
cout<<"push_back"<<endl;
vec1.push_back(*p);
}
}
I'm inserting objects in container class and I've overloaded the == to check two parameters inside the object and if they match I want to update the them and if they don't match I want to put them in the vector, but I don't seem to be able to properly populate my vector, when I do vec1.size() I get 0 even when I insert 3 objects.

You're problem is that your if is inside your search loop. Your if will never be executed, because your loop body never runs, because your .size() will never be greater than 0.
Try this:
// UNTESTED
std::vector<person> vec1;
add(person *p) {
std::vector<person>::iterator it = std::find(vec1.begin(), vec1.end(), *p);
if(it == vec1.end())
vec1.push_back(*p);
else
*it = *p;
}
Or, if you really want to code the loop by hand:
// UNTESTED
std::vector<person> vec1;
add(person *p) {
int i;
for(i=0;i<vec1.size();++i) {
if(vec1[i] == *p)
break;
}
if(i == vec1.size())
vec1.push_back(*p);
else
vec1[i] = *p;
}
Of course, you might consider changing your container. Using a std::map would shorten your code and reduce the time it takes to manipulate large data sets.
std::map<std::string, person> map1;
add(person *p) {
map1[p->name] = *p;
}

When the vec1 starts from empty, the for loop is not going to run. So you want to have at least one element in vec1 to start with. How about add this:
vec1.push_back(*p);
for(int i=0;i<vec1.size();++i){//the rest}

Related

How to create auto iteration in for loop which would assign pointer instead of value to it

I want to create auto iteration loop like this:
std::vector<sometype> vector1;
for(auto it : vector1)
{
if(&it == &vector1.at(4))
//do something...
else
continue;
}
but I found out that adress of "it" when it's equal to vector1.at(4) is not the same as adress of vector1.at(4). How can I do for loop which would assign to "it" not value but adress of value currently pointing to.
You can use reference (add & to variable declarations) to do something like that.
std::vector<sometype> vector1;
for(auto& it : vector1) // add &
{
if(&it == &vector1.at(4))
//do something...
else
continue;
}
Looking at your code as pseudo-code for an algorithm, it basically attempts to solve
Check (via O(n) instead of O(1) cost) if the vector has at least 5 elements.
If true, go to 2.
Otherwise do nothing.
Do something with the 5th element.
If this is indeed the case, you don't actually need a for loop for this, you could just query the size of the vector prior to conditionally accessing (O(1) random access) the 5th element to //do something...:
#include <iostream>
#include <string>
#include <vector>
int main() {
const std::vector<std::string> v{"a", "b", "c", "d", "e", "f"};
if (v.size() > 4) {
auto s = v[4];
// do something with the 5th element, as it exists.
std::cout << e; // d
}
}
Or, you could make use of the bounds-checking element accessor at() clong with exception handling in case of attempted out-of-bounds access.
In the range-based for loop
std::vector<sometype> vector1;
for(auto it : vector1)
{
if(&it == &vector1.at(4))
//do something...
else
continue;
}
it is not an iterator. It is an object of the type sometype that stores the value of the corresponding element in the vector.
Instead of creating an object you should use a reference as for example
std::vector<sometype> vector1;
for( auto &value : vector1)
{
if( &value == &vector1.at(4))
//do something...
else
continue;
}
Pay attention that the else statement with continue does not make a great sense. You could write simply
std::vector<sometype> vector1;
for( auto &value : vector1)
{
if( &value == &vector1.at(4))
//do something...
}
Though it is unclear why you are using the range-based for loop when only one element of the vector will satisfy the condition.

Adding an element to a Vector while iterating over it

As the title says, I want to add an element to a std::vector in certain cases while iterating through the vector. With the following code, I'm getting an error "Debug assertion failed". Is it possible to achieve what I want to do?
This is the code I have tested:
#include <vector>
class MyClass
{
public:
MyClass(char t_name)
{
name = t_name;
}
~MyClass()
{
}
char name;
};
int main()
{
std::vector<MyClass> myVector;
myVector.push_back(MyClass('1'));
myVector.push_back(MyClass('2'));
myVector.push_back(MyClass('3'));
for each (MyClass t_class in myVector)
{
if (t_class.name == '2')
myVector.push_back(MyClass('4'));
}
return 0;
}
EDIT:
Well, I thought for each was standard C++, but it seems that it's a Visual Studio feature:
for each, in
Visual c++ "for each" portability
The act of adding or removing an item from a std::vector invalidates existing iterators. So you cannot use any kind of loop that relies on iterators, such as for each, in, range-based for, std::for_each(), etc. You will have to loop using indexes instead, eg:
int main()
{
std::vector<MyClass> myVector;
myVector.push_back('1');
myVector.push_back('2');
myVector.push_back('3');
std::vector<MyClass>::size_type size = myVector.size();
for (std::vector<MyClass>::size_type i = 0; i < size; ++i)
{
if (myVector[i].name == '2')
{
myVector.push_back('4');
++size; // <-- remove this if you want to stop when you reach the new items
}
}
return 0;
}
As pointed out by pyon, inserting elements into a vector while iterating over it (via iterators) doesnt work, because iterators get invalidated by inserting elements. However, it seems like you only want to push elements at the back of the vector. This can be done without using iterators but you should be careful with the stop condition:
std::vector<MyClass> myVector;
size_t old_size = myVector.size();
for (int i=0;i<old_size;i++) {
if (myVector[i].name == '2') { myVector.push_back(MyClass('4')); }
}
After following the previous answers, you can use const auto& or auto& to have clean code. Should be optimized in release build by the compiler.
std::vector<MyClass> myVector;
std::vector<MyClass>::size_type size = myVector.size();
for (std::vector<MyClass>::size_type i = 0; i < size; ++i)
{
const auto& element = myVector[i];
element.do_stuff();
}

C++ How can I remove pointers refering to the same element in two different std::lists?

I am new in programming c++, so please don't be angry with me if my source code is not exactly brilliant.
I have to write a programm to handle with nodes and edges in a graph for my studies.
I have 2 std::lists in my source code. The first one is to store general Nodes and the other one for saving the kind class of my nodes called ArticleNodes. In general, all elements are pointers to the created objects.
To figure out whether one object is the same in the other list I save the memory address and compare it to the elements on the second list. If there is a match the second element will be deleted.
Now I'd like to delete one element in both lists:
void Graph::deleteNode(unsigned int nodeNumber)
{
list<Node*>::iterator it = m_nodes.begin();
ArticleNode* pCurrentArticleNode;
for(unsigned int i=1; i<nodeNumber; i++) { it++; }
Node* pCurrentNode = (*it);
for (list<ArticleNode*>::iterator itArticle = m_articlenode.begin(); itArticle != m_articlenode.end(); itArticle++)
{
pCurrentArticleNode = (*itArticle);
if(pCurrentNode==pCurrentArticleNode) { m_articlenode.remove(pCurrentArticleNode); }
}
m_nodes.remove(pCurrentNode);
delete pCurrentNode;
delete pCurrentArticleNode;
}
I can compile this, but when I call the function, my programm just exits with return 1.
Actually, I figured out that the remove-command in the if-clause is the problem. Why does that not work??
You should use algorithms more than doing everything manually:
void Graph::deleteNode(unsigned int nodeNumber)
{
assert (nodeNumber < m_nodes.size());
auto it = std::next( m_nodes.begin(), nodeNumber - 1 );
auto itArticle = std::find( m_articlenode.begin(), m_articlenode.end(), *it );
if( itArticle != m_articlenode.end() )
m_articlenode.erase( itArticle );
delete *it;
m_nodes.erase(it);
}
Btw your code deletes the same object twice.
When you remove an element from std::list object with remove( ) method, all iterators pointing to that elements become invalid. In your case, after you remove an element from the list m_articlenode, the iterator object itArticle becomes invalid. And when you increment that iterator, you get an undefined behavior.
Pay attention that the method remove( ) deletes all of the items in the list with given value. So you don't need the for-loop at all. Here is the fixed version of your function:
void Graph::deleteNode(unsigned int nodeNumber)
{
list<Node*>::iterator it = m_nodes.begin();
for(unsigned int i=1; i<nodeNumber; i++) { it++; }
Node* pCurrentNode = (*it);
m_articlenode.remove(pCurrentNode);
m_nodes.remove(pCurrentNode);
delete pCurrentNode;
}
You have to use the erase method of std::list in order to remove an element from your list while iterating over it.
This should do the trick:
void Graph::deleteNode(unsigned int nodeNumber)
{
list<Node*>::iterator it = m_nodes.begin();
ArticleNode* pCurrentArticleNode;
for(unsigned int i=1; i<nodeNumber; i++) { it++; }
Node* pCurrentNode = (*it);
list<ArticleNode*>::iterator itArticle = m_articlenode.begin();
while(itArticle != m_articlenode.end()) {
pCurrentArticleNode = (*itArticle);
if(pCurrentNode==pCurrentArticleNode) {
m_articlenode.erase(itArticle++);
} else {
itArticle++;
}
}
m_nodes.remove(pCurrentNode);
delete pCurrentNode;
delete pCurrentArticleNode;
}
Simple issue in your code is that if you go into the if condition is met and it's body executed, you should come out of the loop. std::remove invalidates the iterator ann you will get issues in next iteration so do:
for (list<ArticleNode*>::iterator itArticle = m_articlenode.begin(); itArticle != m_articlenode.end(); itArticle++)
{
pCurrentArticleNode = (*itArticle);
if(pCurrentNode==pCurrentArticleNode)
{
m_articlenode.remove(pCurrentArticleNode);
break;
}
}
In general, there are other issues in the code. As a first step I would suggest using shared_ptr for managing your Nodes and have list of shared_ptr instead of list of pointers.
I am new in programming c++, so please don't be angry with me if my
source code is not exactly brilliant.
We all start somewhere.
Now I'd like to delete one element in both lists:
OK. Quick question. Why are you deleting article node if it is already deleted (via base Node)? I'm assuming for now node is not duplicated in the list:
My solution below... I've passed the lists as arguments. See comments:
#include <list>
#include <algorithm>
struct Node
{
virtual ~Node(){} //For dyna cast to work...
};
struct ArticleNode : Node
{
};
void deleteNode(std::list<ArticleNode*>& articleList, std::list<Node*>& m_nodes, unsigned int nodeNumber)
{
using namespace std;
if (m_nodes.size() > nodeNumber)
{
auto it = m_nodes.begin();
// Advance advances our iterator by N. No need for your for loop - less risk...
std::advance(it,nodeNumber);
Node* currentNode = *it;
//Casting is bad here, but hey, lets assume if type is wrong, we only erase
// it from Node...(Your call)?
ArticleNode* currentArticleNode = dynamic_cast<ArticleNode*>(currentNode);
if (currentArticleNode)
{
//Use find here.... KISS
auto foundPos = std::find(articleList.begin(), articleList.end(), currentArticleNode);
if (foundPos != articleList.end())
{
//No need to delete currentArticleNode, as we're deleting it already...
articleList.erase(foundPos);
}
//Assuming only one item for now...
}
//Else our node was obviously not the right type, and cannot exist in articleNodes...
m_nodes.erase(it);
delete currentNode;
}
else
{
std::cout << "No such node: " << nodeNumber << std::endl;
}
}

C++ List Insert

I'm trying to insert objects into a list in an ordered manner, based on the objects data member family_id
The first object created skips this function, so there will be at least one object in the list named 'families' before this function is executed.
Running into some trouble with the List insert function... How can you insert before or behind the iterator (itr) using this function? I'm not sure whether to use some push_front / push_back methods, but the if/else statement can get quite long this way...
Would appreciate ideas on how to create this in a simple way.
Family Class
class Family
{
private:
int family_id;
public:
int get_family_id()
{
return family_id;
}
};
Main
list <Family> families;
void insertFamily(int input_id)
{
list<Family>::iterator itr;
for(itr = families.begin(); itr != families.end(); itr++)
{
if (input_id < itr->get_familyid())
{
Family *fam = new Family(input_id);
families.insert(itr, *fam);
// Does this insert the object into the first position of the list,
// bumping the original to the second position?
} else
itr++
}
};
If you're asking what I think you're asking, the insert function will put the value into the position given, and push everything else back. For example:
#include <list>
#include <iostream>
int main(){
std::list<int> my_list;
for(int i = 0; i < 5; i++)
my_list.push_back(i);
std::list<int>::iterator itr = my_list.begin();
++itr;
my_list.insert(itr, 5);
for(itr = my_list.begin(); itr != my_list.end(); ++itr)
std::cout << *itr << " ";
}
Prints
0 5 1 2 3 4
So if you want it to insert BEFORE whatever you stop on, just call the insert as given; if you want to insert AFTER whatever you stop on, call
++itr
and then do the insert.
Edit: If I'm reading your code right, you're also missing a break statement
Family *fam = new Family(input_id);
families.insert(itr, *fam);
break;
^^^^
This breaks the for loop when you're done; without it, you'll keep adding new Families with the same id for every family with a lower index. Also, you don't need the itr++ in the else statement, since it's already being called in your for loop (unless you want to increase it twice when it's not matched?). Then your code might look like:
Main
list <Family> families;
void insertFamily(int input_id)
{
list<Family>::iterator itr;
for(itr = families.begin(); itr != families.end(); itr++)
{
if (input_id < itr->get_familyid())
{
Family *fam = new Family(input_id);
families.insert(itr, *fam);
break; //Added break line
} //Removed else
};
If you want faster insertion, you might look into binary searching, but this won't do much unless you have a very large list. Other than that, your code looks good!

How to remove odd positions from a list?

#include<iostream>
#include<list>
using namespace std;
void compute(int num)
{
list<int> L;
list<int>::iterator i;
list<int>::iterator i2;
int p;
cout<<"Enter the number of numbers\n";
cin>>p;
int a;
for(int k=1;k<=p;k++)
{
cin>>a;
L.push_back(k);
}
cout<<endl;
for(i=L.begin() ; i!=L.end() ; ++i)
{
cout<<*i<<endl;
}
long int k=1;
for(i=L.begin() ; i!=L.end() ; ++i )
{
if(k%2!=0) //This is where I try and delete values in odd positions
{
i2=L.erase(i);
}
k++;
}
for(i=L.begin() ; i!=L.end() ; ++i )
{
cout<<*i<<endl;
}
}
int main()
{
// int testcases, sailors;
//cin>>testcases;
//for(int i=1 ; i<=testcases ; i++)
{
// cin>>sailors;
}
//for(int i=1;i<=testcases;i++)
{
// int num;
//cin>>num;
//compute(num);
}
compute(0);
return 0;
}
I am trying to erase elements using L.erase() function in Lists. But I get an error saying
"Debug assertion failed! ......Expression:list iterator not incrementable"
but we CAN increment iterator right?
erase invalidates the iterator that was passed in as parameter - since the element at the position the iterator was pointing to was just erased! And on that same iterator, an increment is attempted in the next for loop in your code! That's why it fails.
However, erase it will return an iterator pointing to the new position, which we can use; a loop where you erase something from an STL container should therefore look something like the following; I show it with the type you use, list, but you could just as well use e.g. vector:
list<int> L;
// ...
list<int>::iterator it=L.begin();
while (it!=L.end())
{
if(eraseCondition)
{
it=L.erase(it);
}
else
{
++it;
}
}
Or, if possible, it's even better to use std::remove_if:
container.erase(std::remove_if(L.begin(), L.end(), predicate), L.end());
In your case that will be hard - if not impossible - to use since the predicate would need state information (the information whether the index is odd or even). So I'd recommend going with a loop structure as mentioned above; just keep in mind the remove_if for the general case of removing all elements where a certain predicate returns true!
Adding to what wOOte said, you may want to used a reverse iterator to get around the issue.
Technically not in this case.
When you use erase() you delete the node that was pointed to, so you actually invalidate the iterator you were on. So when you increment it it's undefined behavior.
It might be best to create a second list with just the iterators to the positions you'd like to delete, and you can cycle through those and call erase afterward. You wouldn't be erasing the iterators from the second list, so it'd work.
Something like this:
List<IteratorType> deleteList;
//Populate deleteList with every other element from original list.
for (List<IteratorType>::iterator iter = deleteList.begin();
iter !=deleteList.end; ++iter)
{
originalList.erase(*iter);
}
The iterator i is invalidated by the call to erase; however, in the next iteration of the for loop, you try to increment it - this is invalid.
Try
for(i=L.begin() ; i!=L.end() ; )
{
if(k%2!=0) //This is where I try and delete values in odd positions
{
i=L.erase(i);
} else {
++i;
}
k++;
}
instead - only increment the iterator if you don't erase (erase basically "advances" the iterator because it yields an iterator to the element following the one you erased).
You can actually exploit this behaviour of erase to write your function without requiring k:
i = L.begin();
while ( i != L.end() ) {
i = L.erase( i ); // Delete one
if ( i != L.end() ) { // Skip next, if there's an element
++i;
}
}
So you delete the first element, skip the second, delete the third, and so on.