Is it possible to extend the "erase–remove" idiom to work on multiple containers at once?

380 Views Asked by At

In C++, the erase-remove idiom is a great way to delete all elements of a standard container that satisfy a given criterion.

Is it possible to extend the erase-remove idiom to work on multiple containers at once?

That is, can one invoke something similar to erase-remove on one container, and have the corresponding elements in another container removed as well?

In my particular case, the containers are all std::vectors of the same size.

For example, if elements 0, 3, and 5 are deleted from the first container, I would like elements 0, 3, and 5 deleted from the second container as well.

One could, for example, precompute a container that flags the elements to be deleted, build a predicate for remove_if that simply indexes into the flag container, and invoke erase-remove multiple times.

Is it possible to do what I want, without the precomputation?

1

There are 1 best solutions below

1
On

Yes you can do this. It works as is. I provide an example code and explain on it.

// initialises a vector that holds the numbers from 0-9.
  std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
  print(v);

  // removes all elements with the value 5
  v.erase( std::remove( v.begin(), v.end(), 5 ), v.end() ); 
  print(v); 

In this code we want to delete 5 from vector v and we use std::remove and std::erase afterwards. You have to understand what does std::remove do. std::remove swaps elements in container a way that all elements to be deleted go to the end and this function returns an iterator to the first element to be deleted. Next std::erase takes this iterator and removes all elements beginning from this iterator and till the end of a container (actually till the second argument. v.end() in our case).

std::vector<int> v1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
std::vector<int> v2 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 };

auto removeIt=std::remove( v1.begin(), v1.end(), [](const int value){
    return value==0 || value==3 || value==5;
} );

// now v1 = { 1, 2, 4, 6, 7, 8, 9, 0, 3, 5 } and removeIt points to 0 element (next after 9)..
                     // removeIt = ^

std::erase(std::remove_if(v2.begin(),v2.end(),[&](const int value){
    // remove every object that can be found between removeIt and v1.end()
    return std::find(removeIt,v1.end(),value)!=v1.end();
}), v2.end());
// now v2 = { 1, 2, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }
std::erase(removeIt,v1.end());
// now v1 = { 1, 2, 4, 6, 7, 8, 9 }

This code does exactly what you need.