Find an Element by Its Property in std::list

Metin Cakircali

2 min read

The container std::list supports constant-time insertion and removal of elements. The iterators and references are valid after adding, moving, and removing elements (expect the element that is deleted). The downside is that there is no random access. It is implemented as a doubly-linked list, which provides bidirectional iteration capability. Check out the std::forward_list for unidirectional linked list.

City

Let's assume that we have a struct City that has a member name_.

struct City {
    City(const char* name): name_(name) {}
    std::string name_;
};

List of cities

Let's say we have a std::list of cities (Koln and Adana) and we want to look for the city named "Koln" in this list.

std::list<City> cities = {"Koln", "Adana"};

Find by name "Koln"

There are different ways of finding an element in a container. Defined in the C++ Standard Library <algoirthm> header, the std::find_if searches an element in a container using a predicate function (a function that returns bool value).

Option #1 - lambda (since C++11)

Lambda expressions are anonymous function objects. The following lambda compares the city name with "Koln" string.

auto is_koln = [](const City& city) { return city.name_ == "Koln"; };

An example code:

#include <algorithm>
#include <list>
#include <iostream>

struct City {
    City(const char* name): name_(name) {}
    std::string name_;
};

int main()
{
    std::list<City> cities = {"Koln", "Adana"};

    // lambda that checks if the city name is "Koln"
    auto is_koln = [](const City& city) { return city.name_ == "Koln"; };
    
    // find the city "Koln"
    auto it = std::find_if(cities.begin(), cities.end(), is_koln);

    if (it != std::end(cities)) {
        std::cout << "Found city: " << (*it).name_ << "\n";
    } else {
        std::cout << "Not Found!" << "\n";
    }
}

Option #2 - functor (function object)

A functor is implemented by overloading a type's operator() (a.k.a. call operator). It acts like a function but it can hold a state between calls. Below is an example functor is_name that holds name_ and compares it with city name each time called.

struct is_name {
  explicit is_name(const char* name): name_(name) { }

  bool operator()(const City& city) const { return city.name_ == name_; }

private:
  const char* name_;
};

An example code:

#include <algorithm>
#include <list>
#include <iostream>

struct City {
    City(const char* name): name_(name) {}
    std::string name_;
};

namespace pred {

struct is_name {
  explicit is_name(const char* name): name_(name) { }

  bool operator()(const City& city) const { return city.name_ == name_; }

private:
  const char* name_;
};

}  // namespace pred

int main()
{
    std::list<City> cities = {"Koln", "Adana"};
  
    // find the city "Koln"
    auto it = std::find_if(cities.begin(), cities.end(), pred::is_name("Koln"));

    if (it != std::end(cities)) {
        std::cout << "Found city: " << (*it).name_ << "\n";
    } else {
        std::cout << "Not Found!" << "\n";
    }
}