-
October 2nd, 2003, 02:41 PM
#1
How to find & remove object from a std::list ?
I'm trying to find an object in an std::list of objects, and remove it and I was going to look for the NAME of the object as a basis of comparison, something like this:
PHP Code:
class myObj
{
string name;
int A;
int B;
//......
int Z;
}
//then later in the code....
list<myObj> myList;
list<myObj>::iterator itr;
itr = find(myList.begin(),myList.end(), /*look for myObj name */ );
if (itr != myList.end())
{
itr = myList.erase(itr);
}
Question is, how do I lok for just the NAME using the find command?
Thanks for the help!
-
October 2nd, 2003, 03:10 PM
#2
1) I think you want to use find_if() instead of find() , since
you are not checking the ENTIRE object for equality,
just part of the object.
2) the tricky part in creating the predicate, is you want
to pass it the name that you are looking for. There
is a "bnd2nd()" function that does this. I think sample
code will make it clearer :
Code:
#include <list>
#include <string>
#include <functional> // for binary_function and bnd2nd()
#include <algorithm> // for find_if
using namespace std;
class myObj
{
public:
string name;
int A;
int B;
int Z;
};
struct CompareName : public std::binary_function<myObj,std::string,bool >
{
bool operator () (const myObj& obj, const std::string& name) const
{
return obj.name == name;
}
};
int main()
{
list<myObj> myList;
list<myObj>::iterator itr;
// add to list
// remove FIRST occurence of object with name of "Jones"
itr = find_if(myList.begin(),myList.end(), bind2nd(CompareName(),"Jones"));
if (itr != myList.end())
{
itr = myList.erase(itr);
}
return 0;
}
-
October 9th, 2003, 02:01 PM
#3
I wasn't done yet.
Then again if your list gets very large you're better of using a map or set instead.
the cool thing with sets is you can overload the comparison function with your own struct.
struct MyCompare
{
bool operator()(YourObject *p1,YourObject * p2)const
{
//compare anyway your want and return true if bigger,or false if //smaller
}
};
typedef std::set<YourObject*,MyCompare> MySet;
Last edited by fransn; October 9th, 2003 at 02:03 PM.
-
October 9th, 2003, 07:19 PM
#4
Thanks for the help guys!
-
October 10th, 2003, 03:22 AM
#5
For some reason, I've always had problems with bind2nd, so I usually rewrite my functor so that the item to be compared against is a member variable:
Code:
struct CompareName : public std::unary_function<myObj, bool >
{
CompareName(const std::string& n) : name(n) {}
CompareName(const char* s) : name(s) {}
bool operator () (const myObj& obj) const
{
return obj.name == name;
}
private:
std::string name;
};
Then you call it like:
Code:
itr = find_if(myList.begin(),myList.end(), CompareName("Jones"));
Correct is better than fast. Simple is better than complex. Clear is better than cute. Safe is better than insecure.
-- Sutter and Alexandrescu, C++ Coding Standards
Programs must be written for people to read, and only incidentally for machines to execute.
-- Harold Abelson and Gerald Jay Sussman
The cheapest, fastest and most reliable components of a computer system are those that aren't there.
-- Gordon Bell
-
October 10th, 2003, 11:14 AM
#6
Graham,
that's a nice solution, and cerainly makes the calling
sequence more understandable.
-
October 14th, 2003, 04:52 AM
#7
Sorry for bumping the thread but I didn't want to create a new one as I found this one.
I kinda need to do the same thing but the problem with me is that my object name is a private member of myObj class.
I have a get accessor though something like:
Code:
string getObjectName(){return name};
but when I implement it in the code above the following way:
Code:
return obj.getObjectName()== name;
I get the following error:
Code:
passing `
const myObj' as `this' argument of `std::string myObj::getObjectName()'
discards qualifiers
I tried to change around some data types but I still get the same error.
Any help would be appreciated.
Thanks.
Sofia.
Edit:Sorry I forgot to mention that if I use obj.name after taking of name from the private area and to the public my code compiles fine without any errors but I know that it's not the way to do it .
Last edited by megabyte; October 14th, 2003 at 04:56 AM.
-
October 14th, 2003, 05:09 AM
#8
Your problem is that CompareName :: operator() takes as it's argument a const reference to a myObj and your myObj::getObjectName() method is not declared as const. Try changing it's signature to:
Code:
string getObjectName() const {return name};
-
October 14th, 2003, 05:15 AM
#9
Thank you ver much
-
October 15th, 2003, 04:02 AM
#10
Sorry to bother you guys again, but I'm encountring a small problem that I think is due to my lack of knowledge as the whole std::list world is very new to me(I spent so much time learning vectors that I can't grasp well all what's going on with lists).
'nough said, what I'm trying to do is instead of using the following in main()
Code:
itr = find_if(myList.begin(),myList.end(), CompareName("Jones"));
I want to use it in a let's say a member function of myObj.
I do so but I don't know how to keep the changes.Meaning that when I erase a member of the list and try to ouput the list I see that the removal was successful but what I need to do is append the changes to the list I'm working on in my main().
Code:
Myobj.EraseMember(Myobj,"Jones ");
When I define EraseMember(list,key) I make sure to make it's return type as list and then return the list at the end but still the list in the main scope stays unchanged while the one inside the function defenition scope changes.
I'm sure that I'm looking at it the wrong way so please advise.
Thanks in advance.
Sofia.
-
October 15th, 2003, 06:45 AM
#11
It seems to me you're moving into muddy waters here. You store myObjs in a list and then make the individual objects responsible for deleting themselves and eachother from that list. I think there's something wrong with this approach.
Also, your method:
Code:
Myobj.EraseMember(Myobj,"Jones ");
takes as it's first argument a pointer to a myObj even though EraseMember() is a member function and, as such, has access to the this pointer.
What are you trying to do? Erase a myObj from the list through another myObj contained in the same list? I suspect you're complicating matters unnecessarily...
-
October 15th, 2003, 02:16 PM
#12
Sorry if I didn't make myself clear enough but I'll try to catch up.
What I am trying to do is write a member function like let's say pop_front() or pop_back but which would erase an element of the list that is not in the front or the back but somewhere in the middle. the element would be chosen regarding it's name.
so let say I have a list of obj which have a name and an age.
so instead of saying something like
MyList.pop_front(); I would like to be able to pop an element with a name let's say "James". something like MyList.EraseMember("James");
Any help would be really appreciated.
Sofia.
-
October 16th, 2003, 03:52 AM
#13
You're not deriving from std::list are you? If you are see this thread:
STL and derivation
Anyway, you could use containment:
Code:
class MyList
{
public:
typedef list<myObj> OBJLIST, *POBJLIST;
private:
MYOBJLIST m_list;
struct CompareName : public std::unary_function<myObj, bool >
{
CompareName(const std::string& n) : name(n) {}
CompareName(const char* s) : name(s) {}
bool operator () (const myObj& obj) const
{
return obj.getName() == name;
}
private:
std::string name;
};
public:
MyList() {}
virtual ~MyList() {}
myObj pop_first(const string &name)
{
// find_if/erase code here
}
}
Note that this is probably not the only approach, nor is it nessecarily the best. It's just a suggestion.
-
October 16th, 2003, 04:25 AM
#14
Morning everyone,
Thank you for the reply sbrothy.I'm actually deriving from the STL list and eventhough I got to understand why I shouldn't be doing that I just have to do it as I have no choice
I didn't really grasp the idea of containment!! I would really appreciate it if you were to give me an idea on what it means.
Does it have to do with this line??
Code:
typedef list<myObj> OBJLIST, *POBJLIST;
I actually worked up my code a bit and now it compiles but the application crashes
this is the code I have:
Code:
template<class T>
class OrderedList: public list<T> {
public:
OrderedList();
~OrderedList();
void EraseMember(OrderedList<T>& MyList,string myKey);
};
and then in my main I call it this way:
Code:
OrderedList<Myobj> workers;
ifstream inputFile("c:\\NewYork.txt");
ofstream outputFile("C:\\output.txt",ios:: out|ios::trunc);
Myobj anobj;
while(inputFile>>anobj)
Workers.push_back(anobj);
Workers.EraseMember(Workers,"James");
Code:
template<class T>
void OrderedList<T>::EraseMember(OrderedList<T>& MyList,string myKey)
{
typename OrderedList<T>::iterator itr;
itr = find_if(MyList.begin(),MyList.end(), CompareName(myKey));
itr = MyList.erase(itr);
}
once again I know that I am not supposed to inherit from the an stl container(as it's not a base class and hence has no virtual destructors...) .
Thanks for any help.
Sofia.
-
October 16th, 2003, 04:45 AM
#15
The concept of containment is simply that you include the class you would normally derive from as a member of the new class.
Hence:
The class A:
Code:
class A {
public:
int foo() const { return 5; }
}
Derivation from class A:
Code:
class B : public class A {
// foo() is inherited.
}
Containment of class A:
Code:
class B {
private:
A m_a;
public:
int foo() const { return m_a.foo(); } // foo is exposed through the contained class A.
}
Using the latter way is extra coding as you'd have to expose all the A member functions that you'd like to use
from without the class:
Code:
#include <list>
#include <string>
#include <functional> // for binary_function and bnd2nd()
#include <algorithm> // for find_if
#include <iostream>
using namespace std;
class myObj
{
string name;
public:
int A;
int B;
int Z;
void setName(const string &name) { this->name = name; }
string getName() const { return name; }
};
class MyList
{
private:
list<myObj> m_list;
struct CompareName : public std::unary_function<myObj, bool >
{
CompareName(const std::string& n) : name(n) {}
CompareName(const char* s) : name(s) {}
bool operator () (const myObj& obj) const
{
return obj.getName() == name;
}
private:
std::string name;
};
public:
MyList() {}
virtual ~MyList() {}
list<myObj>::size_type size() const
{
return m_list.size();
}
void push_back(const myObj &obj)
{
m_list.push_back(obj);
}
void pop_first(const string &name)
{
// remove FIRST occurence of object with name of 'name' and return the removed element
list<myObj>::iterator itr = find_if(m_list.begin(), m_list.end(), CompareName(name));
if (itr != m_list.end())
m_list.erase(itr);
}
};
int main()
{
MyList li;
myObj o;
o.setName("A");
li.push_back(o);
o.setName("B");
li.push_back(o);
o.setName("C");
li.push_back(o);
cout << li.size() << endl;
li.pop_first("B");
cout << li.size() << endl;
return 0;
}
Ofcourse this would so much simpler if you were allowed to derive from std::list. :-)
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|