Solved

How do I use remove_if?

Posted on 2002-05-23
18
737 Views
Last Modified: 2008-02-01
I want to remove elements from an STL list. For each element on the list (which are pointers), I can use "item->GetState()->IsDeleted()" to check if I need to delete it. How do I make use of that to delete all such items?

All I could find about remove_if are examples that use integers, and put "bind2nd(less_equal<int>(), 10)" in the function call. I understand the general idea of this, but not exactly what it does, so I have no idea how to apply this to what I want to do.
0
Comment
Question by:ET3D
  • 9
  • 5
  • 3
  • +1
18 Comments
 
LVL 4

Expert Comment

by:mblat
ID: 7029954
class r
{
public:
    r(int n) : m_n(n) {}
    int m_n;

    bool IsDeleted( ) { return m_n == 0;  }
};

void printr(r val)
{
    cout << val.m_n << " ";
}

int main()
{
    vector<r> vr;

    vr.push_back(r(0));
    vr.push_back(r(2));
    vr.push_back(r(6));
    vr.push_back(r(0));
    vr.push_back(r(7));
    vr.push_back(r(0));
    vr.push_back(r(4));
    vr.push_back(r(1));

    cout << "Original Vector: ";
    for_each(vr.begin(),vr.end(),printr);

    cout << "Hit any key to remove";
    getch();

    cout << endl << "Result: ";
    vector<r>::iterator i = remove_if(vr.begin(),vr.end(),mem_fun_ref(&r::IsDeleted));
    vr.erase(i,vr.end());

    for_each(vr.begin(),vr.end(),printr);
    getch();
}

Hope it helps...
0
 
LVL 4

Expert Comment

by:mblat
ID: 7029959
Forgot to include headers...

#include <vector>
#include <iostream>

#include <conio.h>

using namespace std;

and then code above...
0
 
LVL 4

Expert Comment

by:mblat
ID: 7029968
Couple more notes:

Expamle is with vector not list, but it should make no difference.  If you are storing pointers then you remove_if line should look like:

    list<r*>::iterator i = remove_if(vr.begin(),vr.end(),mem_fun(r::IsDeleted));
 

and

void printr(r* val)
{
.......
}
0
 
LVL 22

Expert Comment

by:ambience
ID: 7031339
template <class T>
class FlagDel
{
public:
    bool  operator()(T& item)
    {
        if(item->GetState()->IsDeleted())
        {
            delete item;  item = 0;
            return true;
        }
        return false;
    }
};


// somewhere in main

vector< ITEM* > lst;

...

lst.erase( remove_if( lst.begin(), lst.end(), FlagDel<lst::value_type *>()), lst.end() );

OR

lst.erase( remove_if( lst.begin(), lst.end(), FlagDel<ITEM*>()), lst.end() );

hope this helps
0
 
LVL 4

Expert Comment

by:mblat
ID: 7032247
2 ambience...

Arghhh..  question was how to remove items from container, not to delete assitiated memory and remove it from container....

I know that one ususally means another, but not always....
:-)
0
 
LVL 30

Expert Comment

by:Axter
ID: 7032747
mblat,

It looks more like the questioner is asking to delete assitiated memory as well.
See following sentence from question:
>>How do I make use of that to delete all such items?

So ambience might have the right answer, but until we get further input from the questioner, we won't know.


ET3D,
Do you need the data in your container deleted as well as removed from the container?

Is the data in the container pointers or non-pointers?
0
 
LVL 2

Author Comment

by:ET3D
ID: 7033252
As the question said, the data in the container is pointers. Good question about deleting them. I guess I need to do that eventually, although I might want to do something with them first. And also I have the option to delete them in another way.

These technicalities aren't really important, IMO. I much prefer an explanation over a solution of a very specific problem. This is not a school assignment. I'm an experienced enough programmer, and I think I'd be able to build what I wanted if I only understood things better. But I'm new to STL, and the MSDN docs are a joke.

What I want is to know what the parameter of remove_if means -- what its syntax is, and how I use it (or, since you guys seem to like the stand alone version of the function, and not list::remove_if, the third parameter). The docs say something with "binder2nd" or "Pred", but I'm not sure how I get from that to real code. I'd appreciate an explanation or being referred to one.
0
 
LVL 4

Expert Comment

by:mblat
ID: 7033283
1. About remove and remove_if
The meaning of "removal" is somewhat subtle. Remove_if does not destroy any iterators, and does not change the distance between first and last. (There's no way that it could do anything of the sort.) So, for example, if V is a vector, remove_if(V.begin(), V.end(), pred) does not change V.size(): V will contain just as many elements as it did before. Remove_if returns an iterator that points to the end of the resulting range after elements have been removed from it; it follows that the elements after that iterator are of no interest, and may be discarded. If you are removing elements from a Sequence, you may simply erase them. That is, a reasonable way of removing elements from a Sequence is S.erase(remove_if(S.begin(), S.end(), pred), S.end()).
0
 
LVL 4

Expert Comment

by:mblat
ID: 7033289
2. If you are using lists you should mmeber functions of list class, not generic implementation, because member functions work faster on lists.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 30

Expert Comment

by:Axter
ID: 7033302
>>And also I have the option to delete them in another way.

IMHO, this is not a good idea.
If you decide to first delete them, and then erase them out of the container later, you take the chance of using deleted objects.

If you decide to first erase them from the container, and then delete them, that would be impractical, because if you erase them from the container, you no longer have pointers to the objects that you need to delete.
That is, unless you safe a second set of these pointers in an alternate location, but having two sets of a list of pointers, can only lead to double trouble, double bugs, poor optimization, and extra maintenance.

So if you do have pointers stored in a container, and they need to be deleted, the best time to delete them is during the erase process.
That avoid memory leaks and it avoids the chance of you using already deleted objects.
0
 
LVL 4

Expert Comment

by:mblat
ID: 7033304
3. This is sample use

#include <stdafx.h>
#include <list>
#include <iostream>


using namespace std;

class SomeData
{
   
public:
     SomeData(long n) : LastObservation(n) {}
    long LastObservation;
};


template <class T> class not_current_obs : public
std::unary_function<T, bool>
{
    long _CurrentObs;

public:
     not_current_obs(long CurrentObs) : _CurrentObs(CurrentObs) {}

     bool operator( ) ( T& val )
     {
          return val.LastObservation != _CurrentObs;
     }
};

void main()
{
     std::list<SomeData>  ListOfData;
     for (unsigned long i = 0; i < 20; ++i)
          ListOfData.push_back(SomeData(i));

     long    CurrentObservation = 10;
     ListOfData.remove_if(not_current_obs<SomeData>(CurrentObservation));

     const std::list<SomeData>::const_iterator itEnd = ListOfData.end();
     for (std::list<SomeData>::const_iterator it =   ListOfData.begin(); it != itEnd; ++it)
          std::cout << (*it).LastObservation <<  std::endl;
}


Note that this code won't compile on MS platform due to bug in the remove_if implementation shipped with MS comilers....
0
 
LVL 2

Author Comment

by:ET3D
ID: 7033364
Well, I'm using Visual C++ 6, so code that doesn't work there doesn't really help me. Also, an I wrong to assume that list::remove_if DOES remove elements from the list?

And thirdly, as I said before, I'd rather get an explanation and not code. You keep putting things in your code like "not_current_obs", "mem_fun_ref" and so on. I have no idea about them, so your example doesn't help me that much. Let me stress it again -- I don't know STL and I want to understand it. Code examples with things I don't know aren't much help.

Answers that would be of some help would be along the lines of:

"The parameter of list::remove_if is a pointer to a member function of a template bla bla bla. If it's evaluated to 'true'  the element is deleted. This function parameter can be constructed using templates such as X,Y,Z that appear in include file bla.h. X is particularly useful in this case. It means bla bla bla. Y can also be used to bla. You can also construct your own bla by bla bla bla. Here are a few lines of code using list::remove_if with X and Y."

and / or

"Check out http://www.thissiteoranother.org for an in depth explanation of STL. The page with details and example of the parameter construction for remove_if is at http://www.thissiteoranother.org/really_good_explanation.html."
0
 
LVL 30

Expert Comment

by:Axter
ID: 7033371
Both the generic remove_if and the list::remove_if functions do not actucally remove anything.

They do however, move the targeted items to the end of the container.


continue...
0
 
LVL 30

Expert Comment

by:Axter
ID: 7033378
remove_if is an algorithm used to be unwanted objects to the end of the contianer.
The algorithm returns a pointer to the start of the items moved.  If no items were moved, then it just returns end().

continue...
0
 
LVL 30

Expert Comment

by:Axter
ID: 7033411
So if you run the following code, you'll notice that the size of the container remains the same before and after the call to remove_if.

bool TestFunction(const string& Test)
{
     if (Test == "RemoveMe") return true;
     return false;
}

int main(int argc, char* argv[])
{
const int SizeOfItems = 32;
const char Items[][SizeOfItems] = {"One", "RemoveMe", "RemoveMe", "Two", "Three", "Four", "RemoveMe"};
vector<string> Foos;
for( int i = 0;i < sizeof(Items)/SizeOfItems;i++)
{
     Foos.push_back(Items[i]);
}

cout << Foos.size() << endl;

remove_if(Foos.begin(), Foos.end(),TestFunction);

cout << Foos.size() << endl;

system("pause");
return 0;
}
0
 
LVL 4

Accepted Solution

by:
mblat earned 100 total points
ID: 7033706
//Well, I'm using Visual C++ 6, so code that doesn't work there doesn't really help me.

First small clarification
generic remove_if works fine in VC++ STL, just member of list has a bug.

second if you a trying to learn STL you should get youself update for VC++ from http://www.dinkumware.com.

Dinkumware was actually original supplyer of STL implementation for VC++, but for some reasons MS stoped updating it's STL library on VC++ v4.2 ( I think ).
So it's very legimate copy, it fixes not only this bug, but many others.

I think $90 is small price to pay for having good tool, when you trying to learn such comlicated subject as STL.

Axter pretty much explain other thinks to you.

remove and remove_if doesn't remove anything, that's way you neeed to call erase after remove.

as far as SOME explanation of STL check out http://codeguru.earthweb.com/cpp/stlguide/index.shtml

It's far from perfect, but much better than MS docs.  I totally agree here - what a joke they are!!!
0
 
LVL 4

Expert Comment

by:mblat
ID: 7033738
continue

bool TestCondition(int n)
{
   return n == 0;
}

list<int> myList;

myList.psuh_back(0);
myList.psuh_back(1);

list<int>::iterator where = remove_if(myList.begin(), myList.end(),TestCondition);

so this tests list of itegers for values and "removes" all that is == 0 ( of cause we remember that remove doesn't remove anything actually).

no if we want class

class CMyClass
{
public:
CMyClass(int n) : m_nVal(n) {}

int m_nVal;
bool TestCondition(CMyClass* p)
{
   return p->m_nVal == m_nVal;
}
};

list<CMyClass*> myList;

myList.push_back( new CMyList(0) );
myList.push_back( new CMyList(1) );

that's were mem_fun come from - basically it means member function

list<int>::iterator where = remove_if(myList.begin(), myList.end(),mem_fun(CMyClass::TestCondition));

mem_fun is used if you container holds pointers, if it holds actual object
mem_fun_ref is used ( by the way one more MS bug here - it mem_fun_ref doesn;t work up to standard)
0
 
LVL 2

Author Comment

by:ET3D
ID: 7035039
Thanks, mblat! Just what I needed. Those CodeGuru docs I can understand. Microsoft sucks. Anyway, you've been very helpful, and I think I'll be able to implement what I want now. And thanks for your help, too, Axter.
0

Featured Post

What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

Join & Write a Comment

IntroductionThis article is the second in a three part article series on the Visual Studio 2008 Debugger.  It provides tips in setting and using breakpoints. If not familiar with this debugger, you can find a basic introduction in the EE article loc…
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now