Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » C++ : sorting a vector

Credits go to Arthur Kalliokoski, ImLeftFooted, and Stas B. for helping out!
This thread is locked; no one can reply to it. rss feed Print
 1   2   3 
C++ : sorting a vector
William Labbett
Member #4,486
March 2004
avatar

hi,

I've got a vector of class pointers I need to sort according to the value of a member of the class.

I thought of using C stdlib.h qsort but I'm not sure it's meant to be used on members of a C++ vector.

What should I do ?

Arthur Kalliokoski
Second in Command
February 2005
avatar

They all watch too much MSNBC... they get ideas.

ImLeftFooted
Member #3,935
October 2003
avatar

I prefer this method:

#SelectExpand
1class YourWhateverClass { 2... 3}; 4 5class YourWhateverPointer { 6public: 7 YourWhateverClass *ptr; 8 YourWhateverPointer(YourWhateverClass *ptr) : ptr(ptr) {} 9 10 bool operator <(YourWhateverPointer &right) { 11 return ptr->someValue < right.ptr->someValue; 12 } 13}; 14 15int main() { 16 vector<YourWhateverPointer> items; 17 18 items.push_back(YourWhateverPointer(new YourWhateverClass)); 19 items.push_back(YourWhateverPointer(new YourWhateverClass)); 20 items.push_back(YourWhateverPointer(new YourWhateverClass)); 21 22 sort(items.begin(), items.end(); 23}

The value of doing it this way is you'll be able to sort inside other containers, not just vector.

If 'someValue' wont change, than you should use multiset which sorts on insertion for great speed boosts.

int main() {
  multiset<YourWhateverPointer> items;
  
  items.push_back(YourWhateverPointer(new YourWhateverClass));
  items.push_back(YourWhateverPointer(new YourWhateverClass));
  items.push_back(YourWhateverPointer(new YourWhateverClass));

  // items is already sorted.
}

William Labbett
Member #4,486
March 2004
avatar

Thanks.

#SelectExpand
1#ifndef LEVEL_FEATURE_H 2#define LEVEL_FEATURE_H 3 4 5 6enum { PONGPING_FEATURE_C_DRAW, PONGPING_FEATURE_G_DRAW }; 7 8 9 10class LevelFeature { 11 12 13 protected: 14 LevelFeature() {} 15 16 public: 17 18 int feature_type; 19 20 int time_to_delete; 21 22 int draw_priority; 23 24 virtual void draw() const = 0; 25 virtual void update() =0;

That's what the top of my class definition looks like.

So I've got :

vector<LevelFeature *> features_for_level;

I need to sort them for lower values of draw_priority first.

Don't understand the code on the link page.

What should I study ?

I don't know about templates.

/* EDIT */

Thanks Dustin :)

ImLeftFooted
Member #3,935
October 2003
avatar

In my example you would make a LevelFeaturePointer class and implement the operator < on it. The answer is in my last post if you read it carefully.

William Labbett
Member #4,486
March 2004
avatar

No I was asking about Arthur's post. nm.

So if I wanted to access members of

YourWhateverClass

with a

vector<YourWhateverPointer> v;

v.push_back(YourWhateverPointer(new YourWhateverClass));
v.push_back(YourWhateverPointer(new YourWhateverClass));
v.push_back(YourWhateverPointer(new YourWhateverClass));

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

// I'd have to do :

((YourWhateverClass *) v[0].ptr)->member;

Is that right ?

ImLeftFooted
Member #3,935
October 2003
avatar

There's no casting.

v[0].ptr->member

Pro-tip: Try to write code that never needs to leverage type casting.

William Labbett
Member #4,486
March 2004
avatar

Code like this ? :-/

features_for_level.push_back( LevelFeaturePointer( (LevelFeature *) rp ));

Stas B.
Member #9,615
March 2008

The value of doing it this way is you'll be able to sort inside other containers, not just vector.

What are you talking about? Why would you want to wrap a pointer in a special class and provide it with a comparison operator that only makes any sense in the context of sorting and only allows you to sort by one specific property? If you then wanted to sort by another property, would you need to make another pointer wrapper just for that? What does it have to do with being able to sort inside other containers? You're using an overload of the same sort function used in Arthur's example. In short, horrible, horrible idea. OP should totally go with Arthur's example. Why would you even want anything else?

William Labbett
Member #4,486
March 2004
avatar

I'd rather not have to rewrite all my code to deal with the new class, but I can't see how the code Arthur posts would work in my situation.

Stas B.
Member #9,615
March 2008

#SelectExpand
1struct Test { 2 int i; 3 4 Test() 5 { 6 this->i = 0; 7 } 8 9 Test(int i) 10 { 11 this->i = i; 12 } 13}; 14 15bool cmp(Test* t1, Test* t2) { return (t1->i<t2->i); } 16 17int main () { 18 std::vector<Test*> vec; 19 20 vec.push_back(new Test(1)); 21 vec.push_back(new Test(6)); 22 vec.push_back(new Test(3)); 23 vec.push_back(new Test(0)); 24 25 sort(vec.begin(), vec.end(), cmp); 26 27 for(int i = 0; i < vec.size(); i++) 28 printf("%d", vec[i]->i); 29 30 return 0; 31}

Works exactly the same if you change the vector to a list or anything else.

William Labbett
Member #4,486
March 2004
avatar

Magic.

Thanks very much.

...and thanks to Arthur and Dustin too.

ImLeftFooted
Member #3,935
October 2003
avatar

Code like this ?

features_for_level.push_back( LevelFeaturePointer( (LevelFeature *) rp ));

No! Stop casting things. This code will require no casting.

Quote:

What are you t ... angry gibbering ... ing else?

Because you're cluttering your program space with functions everywhere on the global namespace and the definition of sort is not connected to the class. The more you put into the class the better.

I would actually update the code to something like this:

#SelectExpand
1class YourWhateverClass { 2public: 3 4 bool operator <(YourWhateverClass &right) { 5 return someValue < right.someValue; 6 } 7}; 8 9class YourWhateverPointer { 10public: 11 YourWhateverClass *ptr; 12 YourWhateverPointer(YourWhateverClass *ptr) : ptr(ptr) {} 13 14 bool operator <(YourWhateverPointer &right) { 15 return *ptr < *right.ptr; 16 } 17};

And then you can make YourWhateverPointer a generic class with a template parameter. But I'll leave that as an exercise for the reader.

http://www.themusingsofalostprogrammer.com/2012/05/beauty-of-c-is-in-its-sorting.html

Stas B.
Member #9,615
March 2008

Because you're cluttering your program space with functions everywhere on the global namespace and the definition of sort is not connected to the class. The more you put into the class the better.

I like it how you conviniently forget to address my "angry gibbering" even though it makes a bunch of valid points and explains why what you're doing is bad. You still haven't explained what your method has to do with sorting different containers either. While you're at it, please explain why littering the program and global namespace with bogus pointer wrapper classes is any better than littering it with functions? A class has a global identifier name and takes up more space. Finally, explain what stops you from making the comparator function a static member function the class. That would connect it to the class, get it out of the global namespace and is really a pretty obvious thing to do.

Quote:

The real solution to this problem lies in C++11's new move semantics to use MyClass objects in items instead of pointers (really cool stuff, go read about move semantics).

This is from your brilliant article. Sure, move semantics could save you the effort of writing pointer wrappers, but how exactly are they going to help you if you want to sort by different properties in different places in your program? It's pretty common to want to be able to do that.

Quote:

And then you can make YourWhateverPointer a generic class with a template parameter. But I'll leave that as an exercise for the reader.

You very obviously can't because the comparison operator would be different in pretty much every case.

Don't forget to remove your embarassing article. You wouldn't want it to mislead people, would you?

Audric
Member #907
January 2001

edit: missed that StasB has already addressed the point about global namespace.

static bool Test:comparebyheight(Test* t1, Test* t2) { return (t1->height<t2->height); }

axilmar
Member #1,204
April 2001

I've got a vector of class pointers I need to sort according to the value of a member of the class.

#SelectExpand
1#include <algorithm> 2#include <vector> 3using namespace std; 4 5class Foo { 6 int bar; 7}; 8 9bool compare_foos(Foo *a, Foo *b) { 10 return a->bar < b->bar; 11} 12 13int main() { 14 vector<Foo *> foos; 15 std::sort(foos.begin(), foos.end(), compare_foos); 16}

Trent Gamblin
Member #261
April 2000
avatar

Just a side note, the standard libraries will sometimes do random things if the comparator function doesn't address equal values. Handling equal values is called making the sort "stable". So I'd write axilmar's code as:

bool compare_foos(Foo *a, Foo *b) {
   return a->bar <= b->bar;
}

Just the simple addition of the = sign in the comparison.

Stas B.
Member #9,615
March 2008

Just a side note, the standard libraries will sometimes do random things if the comparator function doesn't address equal values. Handling equal values is called making the sort "stable".

As far as I know, "stable" simply means that elements with equal keys keep their relative order after the sort. It usually doesn't matter. How does the addition of the equality sign affect the stability? As far as I know, the "sort" function does an unstable sort in both cases and the "stable_sort" function does a stable sort in both cases. ???

Trent Gamblin
Member #261
April 2000
avatar

Stas B. said:

As far as I know, "stable" simply means that elements with equal keys keep their relative order after the sort.

Yeah, exactly, and that's exactly what the '=' does. Think about it.

EDIT: Also, in many cases is DOES matter, that's why it exists. Try implementing A* with a vector without a stable sort and nobody to tell you the difference. You'll likely go nuts before figuring it out.

Stas B.
Member #9,615
March 2008

I said it USUALLY doesn't matter. Stability depends on the algorithm. Your particular implementation of your particular sorting algorithm of choice needed that correction. The STL stable_sort function does not. There is no single "correct" way. You just need read the documentation to see what a given implementation expects your comparison callback to return, so your point is entirely moot.

Trent Gamblin
Member #261
April 2000
avatar

Yes, entirely. Or you're just mad because I proved you wrong and now you're being a whiney little jerk. All I did was point out something that could be important for the OP to know. You thought you saw an opportunity to show us how smort you are and I rebuffed you. Now go get your mom to make you some cookies and milk and stop being an asshat.

Dizzy Egg
Member #10,824
March 2009
avatar

He is a bit snappy isn't he?

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Arthur Kalliokoski
Second in Command
February 2005
avatar

AFAICT he's just started it in the last couple of months, even though he's been here 4 years. Maybe something else happened.

They all watch too much MSNBC... they get ideas.

Trent Gamblin
Member #261
April 2000
avatar

What? The guy obviously thinks this is some sort of competition, and thinks he's a genius and talks nonsense to try to not look bad. All I did was try to tell William about stable sorts which are important and he tries to shut me down. A real asshat.

Stas B.
Member #9,615
March 2008

Yes, entirely. Or you're just mad because I proved you wrong and now you're being a whiney little jerk. All I did was point out something that could be important for the OP to know. You thought you saw an opportunity to show us how smort you are and I rebuffed you. Now go get your mom to make you some cookies and milk and stop being an asshat.

All you did was to say something completely nonsensical. All I did was to correct you. You rebuffed me? I'm sorry, I totally missed it since you did not actually address anything I said.

Your advice is factually incorrect and misleading. Your suggestion may actually lead to incorrect results. The comparison callback has nothing to do with the stability of the sorting. You just need to provide the appropriate algorithm with the approproate callback function which may or may not need an additional '='. I'm not mad at you or anyone. I'm sorry if you need three spoons of sugar to go with the criticism of your nonsense. I couldn't care less what people here think about my intelligence. I will keep calling you on your bullsh*t and you feel free to call me on mine. Unlike you, I don't hesitate to admit that I'm wrong, apologize and thank whoever corrects me.

[EDIT]

Oh. Excuse me. Did I say "may"? Your suggestion in fact DOES give incorrect results. Run this and see for yourself:

#SelectExpand
1#include <iostream> 2#include <algorithm> 3#include <vector> 4#include <conio.h> 5 6using namespace std; 7 8bool compare_as_ints (double i,double j) 9{ 10 return (int(i)<=int(j)); 11} 12 13int main () { 14 double mydoubles[] = {3.14, 1.41, 2.72, 4.67, 1.73, 1.32, 1.62, 2.58}; 15 16 vector<double> myvector; 17 vector<double>::iterator it; 18 19 myvector.assign(mydoubles,mydoubles+8); 20 21 cout << "using default comparison:"; 22 stable_sort (myvector.begin(), myvector.end()); 23 for (it=myvector.begin(); it!=myvector.end(); ++it) 24 cout << " " << *it; 25 26 myvector.assign(mydoubles,mydoubles+8); 27 28 cout << "\nusing 'compare_as_ints' :"; 29 stable_sort (myvector.begin(), myvector.end(), compare_as_ints); 30 for (it=myvector.begin(); it!=myvector.end(); ++it) 31 cout << " " << *it; 32 33 cout << endl; 34 35 getch(); 36 37 return 0; 38}

 1   2   3 


Go to: