drkg4b
drkg4b

Reputation: 387

How to erase last and/or unique element in a vector?

I need to erase elements from a vector based on their value, I've tried the idiom erase-remove_if but the condition for the item to be erased is not so simple, I've tried something like

map<string, TLorentzVector> map_jets
vector<pair<string,double> > jets_pt


  for( vector<pair<string,double> >::iterator it1 = jets_pt.begin(); it1 != jets_pt.end(); ){
   if( fabs(map_jets[it1->first].PseudoRapidity()) > 2.5 )
      jets_pt.erase(it1);

but I get a segmentation violation when jets_pt has size = 1. The whole program takes data of an experiment and loops over, the map keeps track of the name of the event and the associated variable I need, while the vector stores the string of the map and a value that I need to keep track of. I want to delete from the vector those value which does'n satisfy a few condition

  if( fabs(map_jets[it1->first].PseudoRapidity()) > 2.5 )
      jets_pt.erase(it1);

    if( map_jets[it->first].DeltaR(map_leps["lep1"]) < 0.4 && map_jets[it->first].DeltaR(map_leps["lep2"]) < 0.4 && map_jets[it->first].DeltaR(map_leps["lep3"]) )                                            
        jets_pt.erase(it);  

     if( jets_emfr[k] > 0.9 )                                                                                                                                                                                  
        jets_pt.erase(it);  

Upvotes: 0

Views: 283

Answers (4)

Grizzly
Grizzly

Reputation: 20211

I assume in your real code you actually call operator++ for the iterator somewhere in the loop. However you still have the problem that erase invalidates the iterator, so you would need to do

it = jets_pt.erase(it1);

However the remove_if - erase is really a much more suitable solution here I ould remmend something like the following:

struct remove_functor
{
    map<string, TLorentzVector>& map_jets;
    map<...>& map_leps;

    remove_functor(map<string, TLorentzVector>& m_jets, map<...>& m_leps): map_jets(m_jets), map_leps(m_leps)
    {}

    bool operator()(const pair<string,double>& p)
    {
        return (fabs(map_jets[p.first].PseudoRapidity()) > 2.5) 
           ||  ((map_jets[p.first].DeltaR(map_leps["lep1"]) < 0.4)
             && (map_jets[p.first].DeltaR(map_leps["lep2"]) < 0.4)
             && (map_jets[p.first].DeltaR(map_leps["lep3"]));
    }
 }

Then you can simply use

jets_pt.erase(remove_if(jets_pt.begin(), jets_pt.end(), remove_functor(map_jets, map_leps)), jets_pt.end());

Of course in c++0x you can simply use a lambda function:

auto predicate = [&](const pair<string, double>& p) {bool operator()(const pair<string,double>& p)
    {
        return (fabs(map_jets[p.first].PseudoRapidity()) > 2.5) 
           ||  ((map_jets[p.first].DeltaR(map_leps["lep1"]) < 0.4)
             && (map_jets[p.first].DeltaR(map_leps["lep2"]) < 0.4)
             && (map_jets[p.first].DeltaR(map_leps["lep3"]));
    };
jets_pt.erase(remove_if(jets_pt.begin(), jets_pt.end(), predicate), jets_pt.end());

predicate = [&](...){...}; creates an functor (by means of lambda syntax), which captures all used variables by reference (so map_jets, map_leps,..., indicated by the [&]) which can be used for the remove_if. auto means that the compiler should infer the type of the variable (since we don't have a name for the type generated by the compiler for this lambda).

Upvotes: 1

ZeRemz
ZeRemz

Reputation: 1813

Try the following (approximative c++11, I don't have access to a c++11 conforming compiler on this computer):

jets_pt.erase(
  remove_if(
    jets_pt.begin(),
    jets_pt.end(),
    [&](const pair<string,double> &p) {
      return fabs(map_jets[p.first].PseudoRapidity()) > 2.5;
    }),
  jets_pt.end());

Basically, you want to erase from the iterator returned by remove_if up to the end of the vector. You may edit the predicate to remove_if as needed.

Upvotes: 1

Will Bickford
Will Bickford

Reputation: 5386

Do not erase entries while iterating! You invalidate your loop constraints.

Upvotes: 0

Frerich Raabe
Frerich Raabe

Reputation: 94549

This sounds like a job for the std::remove_if algorithm, which can be used to remove elements matching a given predicate from a container.

Something like this ought to work:

bool myPredicate( pair<string, double> element ) {
  return fabs(map_jets[elem.first].PseudoRapidity()) > 2.5
         || (map_jets[elem.first].DeltaR(map_leps["lep1"]) < 0.4 && ... )    
         || ...
}

jets_pt.erase( remove_if( jets_pt.begin(), jets_pt.end(), myPredicate ),
               jets_pt.end() );

Upvotes: 2

Related Questions