Tuesday, November 27, 2012

Sorting a vector of pointers to objects using STL and C++11 lambdas

One year ago I posted about how to sort a vector of pointers to objects using STL and functors
As I said then, in C++11 we don't need to use functors, we can use lambdas instead. To show how, we'll use the example from that post again.

We have a stl::vector container that contains pointers to objects of some class, and we'd like to sort them based on the value of some member variable using a STL algorithm.
Let's use the Chocolate class again:
#ifndef __CHOCOLATE_H__
#define __CHOCOLATE_H__

#include <string>

class Chocolate {
  public:
    Chocolate(std::string name, double cocoaPercentage, double price)
    {
      this->cocoaPercentage = cocoaPercentage;
      this->price = price;
      this->name = name;
    };
    ~Chocolate();

    double getCocoaPercentage() { return this->cocoaPercentage; };
    double getPrice() { return this->price; };
    std::string getName() { return this->name; };

  private:
    double cocoaPercentage; 
    double price;
    std::string name;
};
#endif /* __CHOCOLATE_H__ */

We'd like to sort the following vector:
vector<Chocolate*> chocolates;
using the STL sort algorithm and two different sorting criteria: by price and by cocoa percentage.

We'll use the version of the sort algorithm that accepts, besides first and last, a third parameter, comp, which according to sort's C++ reference is a:
"Comparison function object that, taking two values of the same type than those contained in the range, returns true if the first argument goes before the second argument in the specific strict weak ordering it defines, and false otherwise."
The only difference is that, this time, instead of sorting our chocolates vector using functors, we'll use C++11 lambdas or anonymous functions (one for each sorting criterion).
According to wikipedia:
"...an anonymous function (also function constant, function literal, or lambda function) is a function (or a subroutine) defined, and possibly called, without being bound to an identifier."
So now we just need to pass a lambda function (the sorting criterion) to the sort function as its third parameter.

Let's see the sorting in action:
#include "Chocolate.h"
#include <vector>
#include <algorithm>
#include <iostream>

using namespace std;

void showChocolates(vector<Chocolate*> & chocolates);

int main(int argc, char **argv)
{
  vector<Chocolate*> chocolates;
  chocolates.push_back(new Chocolate("ChocoKoko", 80., 20.));
  chocolates.push_back(new Chocolate("ChocoLolo", 70., 30.));
  chocolates.push_back(new Chocolate("ChocoBebo", 25., 10.));
  chocolates.push_back(new Chocolate("ChocoBrian", 24., 15.));
  chocolates.push_back(new Chocolate("ChocoMiko", 30., 45.));
  
  cout<<"Sorted by price:"<<endl;
  sort(chocolates.begin(), chocolates.end(), 
    [] (Chocolate *a, Chocolate *b) {
        return a->getPrice() < b->getPrice();
  });
  showChocolates(chocolates);
  
  cout<<endl<<"Sorted by cocoa percentage:"<<endl;
  sort(chocolates.begin(), chocolates.end(), 
    [] (Chocolate *a, Chocolate *b) {
        return a->getCocoaPercentage() < b->getCocoaPercentage();
  });
  showChocolates(chocolates);
  
  for(unsigned int i=0; i<chocolates.size(); ++i) {
   delete chocolates[i];
  }
  return 0;
}

void showChocolates(vector<Chocolate*> & chocolates) {
  for(unsigned int i=0; i<chocolates.size(); ++i) {
    cout<<chocolates[i]->getName()<< " " 
      <<chocolates[i]->getPrice()<<" "
      <<chocolates[i]->getCocoaPercentage()<<"%"<<endl;
  } 
}
We compile it by doing:
$ g++ -std=c++11 Chocolate.h main.cpp -o sorters
And this is the output we get:
$ ./sorters
Sorted by price:
ChocoBebo 10 25%
ChocoBrian 15 24%
ChocoKoko 20 80%
ChocoLolo 30 70%
ChocoMiko 45 30%

Sorted by cocoa percentage:
ChocoBrian 15 24%
ChocoBebo 10 25%
ChocoMiko 45 30%
ChocoLolo 30 70%
ChocoKoko 20 80%
That's how we can sort a vector of pointers to objects using STL and C++11 lambdas.

No comments:

Post a Comment