2

I was thinking about using remove_if on a vector of strings as follows in the pseudo code below:

for(some strings in a given set) {
  remove_if(myvec.begin(), myvec.end(), string_matches_current_string);
}

Now, I am aware I can define the predicate and make this work easily. But I was wondering if there is a standard template function that I could use in place of the predicate above to make this work. I was looking around and couldn't find one. Appreciate any ideas with examples. Thanks!

squashed.bugaboo
  • 1,338
  • 2
  • 20
  • 36
  • 1
    I doubt there is anything of the sort, since `std::vector` is generic, not simply a container for `std::string`s. On that note, just write your own.. It shouldn't be too hard (I'm thinking 4-6 lines?).. – Drise Jun 21 '12 at 01:00
  • OMG... 4-6 lines! What a travesty! :-) In all seriousness though, can't I simply use string::operator==(current string in set)? i.e., as string::operator==(current_string_in_set) as predicate somehow? – squashed.bugaboo Jun 21 '12 at 01:07
  • Maybe I should use some boost function as predicate in the remove_if? Is this even possible? – squashed.bugaboo Jun 21 '12 at 01:10
  • Can you use bind with equals (although this is only C++11)? `std::bind(std::equals(toMatch, _1))` – Jarryd Jun 21 '12 at 01:11
  • Thanks @Jarryd: I am afraid we're not yet at C++11. – squashed.bugaboo Jun 21 '12 at 01:12
  • The equivalent should be possible with boost libraries then. – Jarryd Jun 21 '12 at 01:13
  • @Jarryd Boost is not an available option to all of us; it's helpful to also provide C++ examples, not just pointing to Boost. – Drise Jun 21 '12 at 01:15
  • 1
    Well that's a shame then, the boost version just uses `boost::bind` and the boost _1 placeholder, so it's basically the same. As for doing it without boost the answer about remove works, but if it was anything more complicated I think you would be stuck with writing your own functor. – Jarryd Jun 21 '12 at 01:24
  • 1
    "some strings in a given set" - I take it that you don't mean they're in a `std::set`? If they are, then you're better off with a single call to `remove_if`, instead of one per thing you want removed: `remove_if(vec.begin(), vec.end(), [&](const string &s) { return myset.count(s); });`. – Steve Jessop Jun 21 '12 at 02:02
  • @Steve... no, I didn't actually mean std::set. But nevertheless, that's a great post! Thanks! – squashed.bugaboo Jun 21 '12 at 02:10

2 Answers2

3

I'm pretty sure there isn't a standard function but you can easily write the whole expression using a C++11 lambda:

std::remove_if(myvec.begin(), myvec.end(),
              [&compare_me](std::string const& cmp) -> bool
              {
                return compare_me == cmp;
              });

With compare_me being the "current" string set by the outer loop.

Keep in mind that remove_if returns an iterator to one past the last valid element so in order to get the correct myvec, you have to erase the elements between the iterator returned by remove_if and myvec.end().

For a non-C++11 implementation you'd have to turn the lambda into a function or functor. If you turn it into a functor you can pass the functor directly, if you turn it into a function you'll have to use something like boost::bind to provide the necessary glue.

Timo Geusch
  • 24,095
  • 5
  • 52
  • 70
  • 1
    Mind providing a non C++11 implementation as well? – Drise Jun 21 '12 at 01:06
  • Added some info what to do if you don't have lambdas, but IIRC remove_if is a C++11 construct so a compiler supporting remove_if should also support lambdas. – Timo Geusch Jun 21 '12 at 01:12
  • 1
    I believe you can do the same thing with `std::remove(myvec.begin(), myvec.end(), some_string)` because `std::remove` uses the equal operator, also `remove_if` has been around before c++11. – Jesse Good Jun 21 '12 at 01:15
3

Why using std::remove_if if you already know the value you want to remove? Use std::remove, which removes the items in the provided range, that match the given value:

std::vector<std::string>::iterator new_end = my_vec.end();
for(const auto &current_set_string : some_set)
    new_end = std::remove(myvec.begin(), new_end, current_set_string);
my_vec.erase(new_end, my_vec.end()); // effectively remove them from the vector.

Note that I used the range-based for loop just to make this shorter, but you should use a regular loop if you can't use C++11.

mfontanini
  • 21,410
  • 4
  • 65
  • 73