Counting Algorithms

An introduction to the 5 main counting algorithms in the C++ standard library: count, count_if, any_of, none_of and all_of
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

5b.jpg
Ryan McCombe
Ryan McCombe
Posted

Lets take a look at the some useful algorithms available in the standard library. To access these, we need the <algorithm> header:

#include <algorithm>

In this lesson, we cover the 5 most useful counting algorithms: count, count_if, any_of, none_of and all_of

Let's get started!

std::ranges::count

The std::ranges::count algorithm requires two arguments - the range we want to run the algorithm in, and the element we want to count within that range.

Whether or not an element matches what we’re looking for is determined by an equality check. Therefore, the type of elements in our collection must implement the equality operator, ==

In this example, we count the number of 4s in our vector of integers:

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
  std::vector Numbers { 1, 2, 3, 4, 4, 5 };
  auto Fours { std::ranges::count(Numbers, 4) };
  std::cout << Fours;
}
2

The return type of these algorithms can be somewhat cryptic. For count, the return type is a templated type:

std::ranges::range_difference_t

.Given we’re counting elements in a std::vector<int> in this case, the type returned from count would be even more verbose:

std::ranges::range_difference_t<std::vector<int>> Fours
{
  std::ranges::count(Numbers, 4)
};

It’s common to just use auto in this scenario, as we did in the original example. Alternatively, it’s reasonable to implicitly cast the return value to something simpler, like a 64 bit integer:

int64_t Fours { std::ranges::count(Numbers, 4) };

As we covered in the introduction to range-based algorithms, std::ranges::count and all the other algorithms we cover in this lesson have a variant that accepts starting and ending iterators instead. These are available by omitting the ranges namespace. For example, the iterator variant of std::ranges::count is available as std::count:

auto Fours {
  std::count(Numbers.begin(), Numbers.end(), 4)
};

std::ranges::count_if

The std::ranges::count_if algorithm works slightly differently to count. Rather than passing an element to look for by equality, we pass a function that returns true or false. A function that returns a boolean is also known as a predicate.

Our predicate is going to be called for every element in our range and will pass that element as an argument.

If our predicate returns true, the element is included in the count. If it returns false, the element is not included.

Below, we have a predicate that returns true if a number is even. We pass this predicate to std::ranges::count_if to count the number of elements in our range that are even:

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
  std::vector Numbers { 1, 2, 3, 4, 4, 5 };
  auto isEven{[](int x) { return x % 2 == 0; }};
  auto EvenCount {
    std::ranges::count_if(Numbers, isEven)
  };
  std::cout << EvenCount;
}
3

std::ranges::any_of

The std::ranges::any_of algorithm also accepts a predicate function. The algorithm will return true if our predicate returns true for any element in the range.

In the following example, we return true if any number in our collection is even:

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
  std::vector Numbers { 1, 2, 3, 4, 4, 5 };
  auto isEven{[](int x) { return x % 2 == 0; } };
  bool IncludesEven {
    std::ranges::any_of(Numbers, isEven)
  };

  std::cout << "An even number "
            << (IncludesEven ? "is" : "is not")
            << " included";
}
An even number is included

count_if vs any_of

Algorithms like any_of may seem unnecessary, as we can get the same output with count_if:

bool IncludesEven {
  std::ranges::count_if(Numbers, isEven) > 0
};

The problem with count_if in this scenario is that it is less efficient. Once the first even number is encountered, our boolean is going to be true regardless of what else is in the vector. But count_if is going to continue traversing our vector, doing unnecessary work.

any_of avoids this - as soon as the answer is known, the algorithm will stop and return it.

This is also true of the none_of and all_of algorithms, which we’ll see next.

std::ranges::none_of

The std::ranges::none_of algorithm will run a predicate on everything in our collection, and return true if every predicate returns false.

If our predicate returns true for any element in the collection, the algorithm will stop evaluating further elements, and return false.

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
  std::vector Numbers { 1, 2, 3, 4, 4, 5 };
  auto isEven{ [](int x) { return x % 2 == 0; } };
  bool NoEvenNumbers {
    std::ranges::none_of(Numbers, isEven)
  };

  std::cout << "The range "
            << (NoEvenNumbers ? "contains no" : "contains")
            << " even numbers";
}
The range contains even numbers

This algorithm will return true if the range has no elements:

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
  std::vector<int> Numbers { };
  auto isEven { [](int x) { return x % 2 == 0; } };
  bool Result {
    std::ranges::none_of(Numbers, isEven)
  };

  std::cout << Result ? "true" : "false";
}
true

std::ranges::all_of

The std::ranges::all_of algorithm will run a predicate on everything in our collection, and return true if every predicate returns true.

If our predicate returns false for any element in the collection, the algorithm will stop evaluating further elements, and return false.

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
  std::vector Numbers { 1, 2, 3, 4, 4, 5 };
  auto isEven{[](int x) { return x % 2 == 0; }};
  bool AllEvenNumbers {
    std::ranges::all_of(Numbers, isEven)
  };

  std::cout << "The range is "
            << (AllEvenNumbers ? "all" : "not all")
            << " even numbers";
}
The range is not all even numbers

Note, this algorithm will return true if the range has no elements:

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
  std::vector<int> Numbers { };
  auto isEven { [](int x) { return x % 2 == 0; } };
  bool Result { std::ranges::all_of(Numbers, isEven) };

  std::cout << Result ? "true" : "false";
}
true

Counting Algorithms With Classes

Typically when using these algorithms, the predicates we need will be class methods of the objects contained within our range. When that is the case, we generally don’t need to provide our own predicate function. We can just pass a reference to the class method:

#include <algorithm>
#include <iostream>
#include <vector>

class Character {
 public:
  Character(std::string Name, int Health) :
    Name(std::move(Name)), Health(Health){};

  bool isAlive() const { return Health > 0; }
  string GetName() const { return Name; }

 private:
  std::string Name;
  int Health;
};

int main() {
  std::vector Party {
    Character { "Legolas", 100 },
    Character { "Gandalf", 200 },
    Character { "Gimli", 500 }
  };

  auto EveryoneAlive {
    std::ranges::all_of(Party, &Character::isAlive)
  };

  std::cout << "Everyone "
            << (EveryoneAlive ? "is" : "is not")
            << " alive";
}
Everyone is alive

Counting Algorithms with Projection

All the algorithms in this lesson accept an additional optional argument, which can be a projection function. We cover projection in detail here:

In this example, we project our Character objects to a std::string before passing them to the count algorithm:

#include <algorithm>
#include <iostream>
#include <vector>

class Character {
 public:
  Character(std::string Name, int Health) :
    Name(std::move(Name)), Health(Health){};

  bool isAlive() const { return Health > 0; }
  string GetName() const { return Name; }

 private:
  std::string Name;
  int Health;
};

int main() {
  std::vector Party {
    Character { "Legolas", 100 },
    Character { "Gandalf", 200 },
    Character { "Gimli", 500 }
  };

  auto GimliCount {
    std::ranges::count(
      Party, "Gimli", &Character::GetName
    )
  };

  std::cout << "Gimlis: " << GimliCount ;
}
Gimlis: 1

Was this lesson useful?

Ryan McCombe
Ryan McCombe
Posted
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Standard Library Algorithms
7a.jpg
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access!

This course includes:

  • 106 Lessons
  • 550+ Code Samples
  • 96% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Next Lesson

Minimum and Maximum Algorithms

An introduction to the 7 min/max algorithms in the C++ standard library: clamp, min, min_element, max, max_element, minmax, and minmax_element.
DreamShaper_v7_hipster_Sidecut_hair_modest_clothes_fully_cloth_1.jpg
Contact|Privacy Policy|Terms of Use
Copyright © 2023 - All Rights Reserved