Jack Sparrow
Jack Sparrow

Reputation: 33

Remove a pair from a list of pairs C++

I'm doing this all in classes. I've a private list of pairs:

std::list<std::pair<std::string, size_t>> pokemons_;

To which I've passed certain values as:

{("Pikachu", 25),
    ("Raticate", 20),
    ("Raticate", 20),
    ("Bulbasaur", 1),
    ("Pikachu", 25),
    ("Diglett", 50)};

Now I want to remove a pair by calling a public remove function of my class.

bool PokemonCollection::Remove(const std::string& name, size_t id) {};

I don't understand how to compare the string and id value while calling the remove function:

collection.remove("Raticate", 20);

"collection Is an object of my class"

I've implemented:

bool PokemonCollection::Remove(const std::string& name, size_t id) {
    bool found;
    string x;
    size_t  y;

    for (auto currentPair : pokemons_) {

        pair<string, size_t> currentpair = currentPair;
        x = currentpair.first;
        y = currentpair.second;

        pokemons_.erase(pokemons_.begin() + i)

        for (int i=0; i<pokemons_.size(); i++) {
             if (pokemons_[i].first == x && pokemons_[i].second == y) {
                 //pokemons_.erase(pokemons_.begin() + i);
                 cout<<"FOUND!!!!!!!!!!!!!!!";
                 found = true;
                 return true;
            }
            else {
                found = false;
            }
        }
    }
    return found;
}

But this remove function of mine gives some errors I don't really understand.

Also it gives so many errors on the commented line where I used the erase function. How can I compare the string and id and remove that pair from original private list of my class?

MY FUNCTION

bool PokemonCollection::Remove(const std::string& name, size_t id) {

    bool found;
    //string x;
    //size_t y;

    pokemons_.remove_if([&](std::pair<std::string, size_t>& p) {
        return found = true and p.first==name and p.second==id;
    });

    if(found == true) {
        return true;
    }
    else {
        found = false;
    }
    return found;
}

Upvotes: 1

Views: 809

Answers (2)

A M
A M

Reputation: 15265

There is a very simple solution to your problem.

The std::listhas a function remove_if, which will do everything for you. See here.

Please see the below code as an example:

#include <algorithm>
#include <iostream>
#include <string>
#include <utility>
#include <list>

std::list<std::pair<std::string, size_t>> poke
        {{"Pikachu", 25}, 
        {"Raticate", 20}, 
        {"Raticate", 20}, 
        {"Bulbasaur", 1}, 
        {"Pikachu", 25}, 
        {"Diglett", 50}};

void remove(const std::string& s, size_t i) {
    
    poke.remove_if([&](std::pair<std::string, size_t>& p){return p.first== s and p.second==i;});
}

int main() {
    remove("Raticate", 20);

    for (const auto& [s,i] : poke) 
        std::cout << s << '\t' << i <<'\n';
}

Some more information:

As you can read in the remove_if documentation of the std::list, it needed to be called as following:

void remove_if( UnaryPredicate p );

The problem for you maybe the "UnaryPredicate". We can read:

unary predicate which returns ​true if the element should be removed.

and, we can read further:

The expression p(v) must be convertible to bool for every argument v of type (possibly const) T, regardless of value category, and must not modify v. Thus, a parameter type of T&is not allowed, nor is T unless for T a move is equivalent to a copy (since C++11). ​

But this will also not help you very much. Basically a predicate is, extremely simplified, a function (object).

So, remove_if will iterate over all elements in the std::list and call this "function". If this "function" returns true, then then the associated list-element will be removed.

The C++ standard defines Predicate as follows (25/7):

The Predicate parameter is used whenever an algorithm expects a function object that when applied to the result of dereferencing the corresponding iterator returns a value testable as true. In other words, if an algorithm takes Predicate pred as its argument and first as its iterator argument, it should work correctly in the construct if (pred(*first)){...}. The function object pred shall not apply any non-constant function through the dereferenced iterator. This function object may be a pointer to function, or an object of a type with an appropriate function call operator.

For the above case, I used a lambda expression as function object. This mechanism is widely used in C++. Please read about that.


Unfortunately remove_if will not return any value. There are really several methods to build a solution here.

Let me show you one solution, by still using the lambda.

#include <algorithm>
#include <iostream>
#include <string>
#include <utility>
#include <list>

std::list<std::pair<std::string, size_t>> poke
{ {"Pikachu", 25},
{"Raticate", 20},
{"Raticate", 20},
{"Bulbasaur", 1},
{"Pikachu", 25},
{"Diglett", 50} };

bool remove(const std::string& s, size_t i) {

    bool found = false;
    poke.remove_if([&](std::pair<std::string, size_t>& p) {bool rv = (p.first == s and p.second == i); if (rv) found = true; return rv; });
    return found;
}

int main() {
    if (remove("Raticate", 20))
        std::cout << "\nFound\n\n";
    else
        std::cout << "\nNot Found\n\n";

    for (const auto& [s, i] : poke)
        std::cout << s << '\t' << i << '\n';
}

Upvotes: 2

G. Sliepen
G. Sliepen

Reputation: 7973

It can be done even simpler than A M's solution. std::pair<> comes with comparison operators, so you don't need a custom function to check if an element of the list is equal to a given pair:

void remove(const std::string& s, size_t i) {
    poke.remove({s, i});
}

Since C++20, std::list's remove() and remove_if() return a count of the number of elements removed. If you need to be compatible with earlier versions, you could always check the result of poke.size() before and after the call to remove() to see if the size of the list has changed.

Upvotes: 0

Related Questions