8

say I have a class "Information" and it stores the Name and Age of people in a vector.

so...

class Information {

private:
int age;
string name;

//etc, etc...
};

How would I sort the vector in either ascending/descending order with respect to age?

I believe you use something like this.

sort(listOfPeople.begin(), listOfPeople.end(), greater<Information>());

listOfPeople would be the vector.

Any help would be greatly appreciated.

user432584920684
  • 379
  • 1
  • 8
  • 19

6 Answers6

6

If you want to sort them in non-descending order by age, one way to do it is to define a functor for comparison:

class CompareInformations {
    public:
    // after making CompareInformations a friend class to Information...
    operator(const Information& rhs, const Information& lhs) {
        return rhs.age < lhs.age;
    }
};

And then do your sort:

sort(listOfPeople.begin(), listOfPeople.end(), CompareInformations());

You could also overload operator< for your class, and do without the comparison object:

// inside your class
bool operator <(const Information& rhs) {
    return age < rhs.age;
}

Then sort it:

sort(listOfPeople.begin(), listOfPeople.end());

The above examples assume you want to sort in non-descending (almost ascending, but not quite) order. To do non-ascending order, just change all occurrences of < to >.

Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • What do you mean by making CompareInformations a friend class to Information? – user432584920684 Aug 18 '11 at 04:26
  • @Vincent as you have it, `age` is a private variable and the class `CompareInformations` accesses that variable. That would give you an error because that's not allowed. However, if you put `friend class CompareInformations;` somewhere in the definition of `Information`, you'll let `CompareInformations` access the private members of that class. Or you could just replace the accesses to `rhs.age` and stuff with `rhs.getAge()` or whatever accessor you have defined. – Seth Carnegie Aug 18 '11 at 04:28
  • Yep, I used rhs.getAge(). I attempted it however I get the following error message when attempting to compile. error: passing 'const Information' as 'this' argument of 'int Information::getAge()' discards qualifiers testone.cpp – user432584920684 Aug 18 '11 at 04:31
  • @Vincent yeah, have you tried marking the `getAge` method as `const`? Only `const` functions can be called on `const` objects. That, or remove the `const` from the arguments of the comparison functions. – Seth Carnegie Aug 18 '11 at 04:34
  • Sigh. When I try removing the const from the arguments of the comparison functions, I now get the following error. testone.cpp: In function 'int main()': testone.cpp:25: error: too few arguments to function 'bool CompareAges(Information&, Information&)' testone.cpp:74: error: at this point in file – user432584920684 Aug 18 '11 at 04:42
  • @Vincent you're trying to mix Ransom's answer with mine. If you're using the plain function version like Ransom's answer (i.e. no class or operator overloading) then change `sort(listOfPeople.begin(), listOfPeople.end(), CompareAges());` to `sort(listOfPeople.begin(), listOfPeople.end(), CompareAges);` See that I removed the parentheses after `CompareAges`. – Seth Carnegie Aug 18 '11 at 04:44
  • I got it. Thank you so much for your help! Much appreciated. Just a question though, what exactly are the negatives of having getAge() as const? I've never actually seen this before. – user432584920684 Aug 18 '11 at 04:51
  • 1
    @Vincent You should take a look at the [C++ FAQ: Const correctness](http://www.parashift.com/c++-faq-lite/const-correctness.html), especially [What is a "const member function"?](http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.10). In fact read through the entire FAQ when you have some time, it's an excellent resource. –  Aug 18 '11 at 04:55
  • I would make it top priority to read the whole FAQ if you haven't already. – Seth Carnegie Aug 18 '11 at 04:57
4

You need to create a comparator function or functor class that takes two Information references and returns true if the first should be ordered before the second.

The following will sort from oldest to youngest:

bool CompareAges(const Information & left, const Information & right)
{
    return left.age > right.age;
}

std::sort(listOfPeople.begin(), listOfPeople.end(), CompareAges);

To select whether to sort ascending or descending, you can have two different calls to sort with different comparison functions, or you can create a functor class that has a flag determining how the items should be sorted.

struct CompareAgesUpOrDown
{
    CompareAgesUpOrDown(bool bDown) : m_bDown(bDown) {}
    bool operator() (const Information & left, const Information & right)
    {
        if (m_bDown)
            return left.age < right.age;
        else
            return left.age > right.age;
    }
    bool m_bDown;
};

bool bDown = ...;
std::sort(std::sort(listOfPeople.begin(), listOfPeople.end(), CompareAgesUpOrDown(bDown));
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • I'm not sure if I'm doing it correctly but attempting to implement the first block of code gives me the following output when trying to compile.testone.cpp:27: error: passing 'const Information' as 'this' argument of 'int Information::getAge()' discards qualifiers testone.cpp:27: error: passing 'const Information' as 'this' argument of 'int Information::getAge()' discards qualifiers – user432584920684 Aug 18 '11 at 04:23
  • Sorry, I'm fairly new at this. How exactly would I mark getAge as const ? – user432584920684 Aug 18 '11 at 04:31
  • 1
    You mark a function as const by putting `const` at the end of the function prototype: `int getAge() const`. By doing that you are "promising" that function will not modify any members of the class to which it belongs. –  Aug 18 '11 at 04:50
  • I got it. Thank you so much for your help. I assume this would also work for comparing time_t as opposed to int? Again, thanks! – user432584920684 Aug 18 '11 at 05:01
  • @Vincent yes, you're correct, since `time_t` is an integral type (it's some kind of a number). – Seth Carnegie Aug 18 '11 at 05:03
  • @Vincent, yes time_t should work just as well as int. Anything with a working `<` operator. – Mark Ransom Aug 18 '11 at 05:05
4

Others have already shown C++98/03 solutions. In C++11, you might want to use a lambda for your comparison instead:

// ascending age:
std::sort(people.begin(), people.end(), 
          [](person const &a, person const &b) { return a.age < b.age; });

// descending age:
std::sort(people.begin(), people.end(), 
          [](person const &a, person const &b) { return b.age < a.age; });

And, in case it should happen to arise:

// ascending name:
std::sort(people.begin(), people.end(), 
          [](person const &a, person const &b) { return a.name < b.name; });

// descending name:
std::sort(people.begin(), people.end(), 
          [](person const &a, person const &b) { return b.name < a.name; });

IMO, Information is too generic a name, so I've changed it to person. Contrariwise, listOfPeople puts too much emphasis on the form rather than the content (and, worse, it's just plain wrong, since you really have a vector of people, not a list at all). IMO, in programming, it's generally better to use list only to refer to a linked list, not to linear data structures in general.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Thanks, I will keep your tips in mind. Just a question, I assume you do not need the bool comparing function if you implement it this way? I will try out this code when I get home. – user432584920684 Aug 18 '11 at 06:19
  • @Vincent: yes. A lambda lets you specify the comparison code inline instead of separately. FWIW, if you're usually using one ordering, you probably want to put that into the class' `operator<`, and only explicitly specify the order for other cases. – Jerry Coffin Aug 18 '11 at 06:30
1

You need to have a comparison function or object in order to use that sort. Take a look at the sort page at cplusplus.com for examples and information.

Here's a complete example using a comparison function:

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

class Information {
public:
  Information(int age, std::string name) : m_age(age), m_name(name) {}
  int age() const { return m_age; }
  std::string name() const { return m_name; }
private:
  int m_age;
  std::string m_name;

  friend bool sortInformationByAgeAscending(const Information& lhs, 
                                            const Information& rhs);      
  friend bool sortInformationByAgeDescending(const Information& lhs, 
                                             const Information& rhs);
};

bool sortInformationByAgeAscending(const Information& lhs, 
                                   const Information& rhs) {
  return lhs.m_age < rhs.m_age;
}

bool sortInformationByAgeDescending(const Information& lhs, 
                                    const Information& rhs) {
  return lhs.m_age > rhs.m_age;
}
int main (int argc, const char * argv[])
{
  std::vector<Information> info;
  info.push_back(Information(1, "Bill"));
  info.push_back(Information(5, "Ann"));
  info.push_back(Information(2, "Sue"));

  std::sort(info.begin(), info.end(), sortInformationByAgeAscending);

  std::cout << info.at(0).age() << ": " << info.at(0).name() << std::endl;
  std::cout << info.at(1).age() << ": " << info.at(1).name() << std::endl;
  std::cout << info.at(2).age() << ": " << info.at(2).name() << std::endl;

  return 0;
}
0

For descending sort you must overload > operator and not < then call sort with greater option like this : sort(listOfPeople.begin(), listOfPeople.end(), greater<Information>());

0

I would overload operator < and then sort using greater. Greater essentially means rhs < lhs. You then sort using sort(listOfPeople.begin(), listOfPeople.end(), greater<Information>());

If you decide to add operator< your class would play nicely with std::set and as a key in std::map in addition to allowing sort.

class Information {

private:
int age;
string name;

friend bool operator< (Information const& lhs, Information const& rhs){
    return lhs.age < rhs.age;
}

//etc, etc...
};
Flame
  • 2,166
  • 2
  • 20
  • 40