C++ List Insert - c++

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!

Related

How to add the results from the iteration to new multimap in c++?

How to insert result into a new multimap?? I am trying to search over dictionary container to find keyword given by the user and I need to iterate over the tempCollector to find distinct elements. But, I can't seem to find a way to store the results in tempCollector.
//current container with all the data
multimap<string, DictionaryItem> dictionaryContainer;
void search(vector<string> input) {
if (dictionaryContainer.empty()) {
cout << "it's empty";
}
int inputSize = input.size();
string tempKeyword = input[0];
//need to copy or insert the value and keys to the tempCollector
multimap<string, DictionaryItem>tempCollector;
//iteration to find keyword; want to store the result in tempCollector
auto its = dictionaryContainer.equal_range(tempKeyword);
for (multimap<string, DictionaryItem>::iterator itr =its.first; itr != its.second; itr++) {
itr->second.print();
}
};
If you want to copy the whole its range:
tempCollector.insert(its.first, its.second);
If you want to copy each element:
for (auto itr =its.first; itr != its.second; itr++) {
if (condition) {
tempCollector.emplace(tempKeyword, itr->second);
//or
tempCollector.insert(*itr);
//or
tempCollector.emplace(tempKeyWord, DictionaryItem(...));
}
}
Keep in mind (multi)map handles pairs of key/values as std::pair<Key,T> (aka value_type).
The multimap::insert method assumes you've already constructed the pair(s) to insert, while multimap::emplace will build them for you.

Finding most common element in a list (C++ STL)?

I have a program where I have to find the most common element in a list of integers. I do this with the program below, but the problem is, I suspect that the erase function messes up with the iterator incrementation in the countRepetition() function. My question is how can I fix the problem or if this is not the issue what is it?
Thanks in advance.
You have a couple issues. First, as you suspected, was the incorrect use of erase. When you erase an iterator it invalidates the iterator. Any use of the iterator afterwards is undefined behavior. Since erase returns the next valid iterator what you can do is restructure the loop like
for (START = l.begin(); START != l.end();) { // do not increment here
if (*START) {
counter++;
START = l.erase(START); // erase and get next
}
else
{
++START; // go to next
}
}
So now at least you loop through the list. Unfortunately you will still have an invalid iterator in main. You pass START from main to countRepetition and when that iterator is erased from the list you then have an invalid iterator. What you need to do is get a new begin iterator from the list each iteration since you are always erasing the first element. That would make your for loop look like
for (START = l.begin(); START != l.end(); START = l.begin()) {
m.push_back(countRepetition(START));
}
Another issue is you just check if the character is not 0. If you are counting repetitions you need to make sure you are checking that the iterator is the same character. I'll leave that for you to implement.
I would also like to point out there is an easier way to do all of this. A std::map lets you build a histogram very easily. Combine that with std::max_element and you could write your entire program as
int main()
{
std::map<char, int> histogram;
while ('0' != (number = getchar()))
++histogram[number]; // add to map, increment count of occurances
auto most_frequent = *std::max_element(histogram.begin(),
histogram.end(),
[](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; }).first;
std::cout << most_frequent;
return 0;
}
Your problem is that you use global variables everywhere.
The global START is changed in two loops, so you only access the first loop once, then it is changed again in the second function and you don't execute the first loop a second time.
Why do you use the global variables? You should not use them but use local variables.
This is probably what you are looking for:
#include <iostream>
#include <list>
#include <vector>
#include <map>
using namespace std;
list <char> l;
map<char, int> ans;
int main()
{
char c;
do{
c = getchar();
l.push_back(c);
}while(c != '0');
for(auto chr: l){
ans[chr]++;
}
char ch;
int mx = 0;
for(auto k: ans){
if(k.second > mx)
{
ch = k.first;
mx = k.second;
}
}
cout<<ch<<" : "<<mx;
}

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;
}
}

Loop for container class 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}

List Iterator Remove()

I have a list iterator that goes through a list and removes all the even numbers. I can use the list iterator to print out the numbers fine but I cannot use the list's remove() and pass in the dereferenced iterator.
I noticed that when the remove() statement is in effect, *itr gets corrupted? Can somebody explain this?
#include <iostream>
#include <list>
#define MAX 100
using namespace std;
int main()
{
list<int> listA;
list<int>::iterator itr;
//create list of 0 to 100
for(int i=0; i<=MAX; i++)
listA.push_back(i);
//remove even numbers
for(itr = listA.begin(); itr != listA.end(); ++itr)
{
if ( *itr % 2 == 0 )
{
cout << *itr << endl;
listA.remove(*itr); //comment this line out and it will print properly
}
}
}
There are a few issues with your code above. Firstly, the remove will invalidate any iterators that are pointing at the removed elements. You then go on to continue using the iterator. It is difficult to tell which element(s) remove would erase in the general case (although not in yours) since it can remove more than one.
Secondly, you are probably using the wrong method. Remove will iterate through all of the items in the list looking for any matching elements - this will be inefficient in your case because there is only one. It looks like you should use the erase method, you probably only want to erase the item at the position of the iterator. The good thing about erase is it returns an iterator which is at the next valid position. The idiomatic way to use it is something like this:
//remove even numbers
for(itr = listA.begin(); itr != listA.end();)
{
if ( *itr % 2 == 0 )
{
cout << *itr << endl;
itr=listA.erase(itr);
}
else
++itr;
}
Finally, you could also use remove_if to do the same as you are doing:
bool even(int i) { return i % 2 == 0; }
listA.remove_if(even);
You can't use an iterator after you delete the element it referred to.
However, list iterators which refer to non-deleted items after a remove() should remain valid.
Could we use something like this:
container.erase(it++);
I tried on this example:
int main(){
list<int>*a=new list<int>;
a->push_back(1);
a->push_back(2);
a->push_back(3);
list<int>::iterator I;
I=a->begin(); ++I;
a->erase(I++);
cout<<*I<<endl;
}
and it displayed 3, as I wanted. Now I don't know if this is valid or one of those which "sometimes work and sometimes not".
EDIT: Maybe it is because of compiler. For example, compiler I am using (GNU gcc-g++) is treating lists (std::) as circular, ie if I increase iterator after list->end() it puts you at the beginning.
Since iterators depend on the length of the structure remaining the same, most iterators do not allow a list to be changed while the iterator is in use. If you want to go through and change the list, you're going to have to use a loop independent of the iterator.