Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I'm a spoiled Python programmer who is used to calculating the argmax of a collection with respect to some function with

max(collection, key=function)

For example:

l = [1,43,10,17]
a = max(l, key=lambda x: -1 * abs(42 - x))

a then contains 43, the number closest to 42.

Is it possible to write a C++ function which takes any "iterable" and any function and returns the argmax like above? I guess this would involve template parameters, the auto keyword, and range-based iteration, but I was not able to piece it together.

@Nawaz It creates a function which maps the argument x to the absolute difference between 42 and x. Multiply by -1, because we want to minimize the difference. – clstaudt Jan 7, 2013 at 16:27 Please note that your python code does not exactly match the definition of argmax you referenced as it returns a single value, not a set. – mkluwe Jan 7, 2013 at 16:37 your question is for finding the max, not argmax. (argmax : index of max value). For c++ std::vector argmax, see fundamentalthinking.blogspot.com/2016/04/… – Chan Kim Nov 25, 2020 at 4:21

This is a two-step process. Define a function key which should get mapped to the elements, i.e. which is applied before the operation which finds the maximum. Wrap things together in a lambda expression defining the comparison for finding the maximum.

auto key = [](int x){
    return -abs(42 - x);
std::max_element(l.begin(), l.end(), [key](int a, int b){
    return key(a) < key(b);

Here, we have to capture key which was defined outside the second lambda function. (We could also have defined it inside). You can also put this in one single lambda function. When the 42 should be parameterized from outside the lambda, capture this as a variable:

int x = 42;
std::max_element(l.begin(), l.end(), [x](int a, int b){
    return -abs(x - a) < -abs(x - b);

Note that std::max_element returns an iterator. To access the value / a reference to it, prepend it with *:

int x = 42;
auto nearest = std::min_element(l.begin(), l.end(), [x](int a, int b){
    return abs(x - a) < abs(x - b);
std::cout << "Nearest to " << x << ": " << *nearest << std::endl;

You can nicely wrap this in a generic find_nearest function:

template<typename Iter>
Iter find_nearest(Iter begin, Iter end,
                  const typename std::iterator_traits<Iter>::value_type & value)
    typedef typename std::iterator_traits<Iter>::value_type T;
    return std::min_element(begin, end, [&value](const T& a, const T& b){
        return abs(value - a) < abs(value - b);
auto a = find_nearest(l.begin(), l.end(), 42);
std::cout << *a << std::endl;

Live demo find_nearest: http://ideone.com/g7dMYI

A higher-order function similar to the argmax function in your question might look like this:

template<typename Iter, typename Function>
Iter argmax(Iter begin, Iter end, Function f)
    typedef typename std::iterator_traits<Iter>::value_type T;
    return std::min_element(begin, end, [&f](const T& a, const T& b){
        return f(a) < f(b);

You can invoke this with the following code, having exactly the lambda function from your question:

auto a = argmax(l.begin(), l.end(), [](int x) { return -1 * abs(42 - x); });
std::cout << *a << std::endl;

Live demo argmax: http://ideone.com/HxLMap

The only remaining difference now is that this argmax function uses an iterator-based interface, which corresponds to the design of the C++ standard algorithms (<algorithm>). It's always a good idea to adapt your own coding style to the tools you're using.

If you want a container-based interface which returns the value directly, Nawaz provided a nice solution which requires the decltype-feature to correctly specify the return type. I decided to keep my version this way, so people can see the both alternative interface designs.

Nice answer, but the "find nearest" problem was just an example for the use of max(collection, key=function), not the actual problem to be solved. – clstaudt Jan 7, 2013 at 16:48 @cls Here you go. The .begin() and .end() thing are typical for C++ algorithms, so I adopted the C++ style. – leemes Jan 7, 2013 at 16:55 @cls: Also note that this solution returns the iterator to the element rather than element itself. – Nawaz Jan 7, 2013 at 17:19 You should use typedef std::iterator_traits<Iter>::value_type T; to be correct, which is specialized for pointer iterators that don't have an inner-type value_type. Otherwise this answer's solution is the right way to go. – GManNickG Jan 7, 2013 at 18:32

Since @leemes solutions are too many. All are correct, except that none attempts to imitate the Python version in your example, Here is my attempt to imitate that:

Convenient generic argmax-function just like Python version:

template<typename Container, typename Fn>
auto max(Container const & c, Fn && key) -> decltype(*std::begin(c))
    if ( std::begin(c) == std::end(c) ) 
       throw std::invalid_argument("empty container is not allowed.");
    typedef decltype(*std::begin(c)) V;
    auto cmp = [&](V a, V b){ return key(a) < key(b); };
    return *std::max_element(std::begin(c), std::end(c), cmp);

And use it as:

std::vector<int> l = {1,43,10,17};
auto a = max(l, [](int x) { return -1 * std::abs(42-x); };
int l[] = {1,43,10,17}; //works with array also!
auto a = max(l, [](int x) { return -1 * std::abs(42-x); };

Note: Unlike the other solution, this max() returns the element itself, not the iterator to the element!

Also note this solution would work for user-defined container also:

namespace test
     template<size_t N>
     struct intcollection
         int _data[N];
         int const * begin() const { return _data; }
         int const * end() const { return _data + N; }
test::intcollection<4> c{{1,43,10,17}};
auto r = max(c, [](int x) { return -1 * std::abs(42-x); });

See the live demo.

@cls: And decltype deduces the type of the expression. So you can write decltype(0) age = 40; which is same as int age = 40;. In this case, it doesn't make much sense, but sometimes it is needed like in my solution. – Nawaz Jan 7, 2013 at 17:00 If you are gonna use std::begin, do it with ADL! So people can write custom containers. :) Second, note that your code executes undefined behavior on empty containers, which I doubt the python version did. – Yakk - Adam Nevraumont Jan 7, 2013 at 18:14

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.