tags:

views:

281

answers:

5

hi,

I want to sort points_vec vector as shown in the pseudocode below. I want to sort this vector, by a coordinate value like x or y or z

class A{

  std:vector<double*> points_vec;

  void doSomething();

}

Then, in method A::doSomething, I want sort this vector:

void A::doSomething() {
    std::sort(points_vec.begin(), points_vec.end(), sortPoints());
}

Can someone please show me syntax for the sortPoints() method.. Preferably I want it to be a method of class A. this post creates a struct to do this, not sure if I should create a similar struct within the class. Is there another way to handle this?

thanks

+2  A: 

First of all - what you have will break all your points - as you'll sort by single doubles not by "points consisting of 3 doubles".

The best way to do this I think is:

  1. Store the points as some Point3D class not a couple doubles
  2. Define the less then operator for Point3D
  3. Just call std::sort(points_vec.begin(), points_vec.end() );

If you'd want to sort them by in different ways that's when you'd use the sort functor and create different functors with operators() for different purposes.

RnR
@RnR : Thanks ffor the answer. I would appreciate if you could post a syntax e.g. syntax for less than operator. Also what will be the syntax if I continue to use double* ?
memC
@memC - as to syntax you have it in a couple posts now - defining the () operator is the key to functors - I'd also recommend the SGI STL page: http://www.sgi.com/tech/stl/ - good luck
RnR
@RnR: Thank you for your suggestion! I will be implementing the Point class to handle this. Very useful suggestion.
memC
+5  A: 

The simplest way is to provide a functor which is used by the sort algorithm to compare two values. You can write like this:

struct Compare
{
  bool operator()(double* first, double* second) const
 {
   //Compare points here
 }
};

And use like:

std::sort(p.begin(), p.end(), Compare());

EDIT for comment by OP: Yes, this sample code compiles fine:

class A
{
public:
    struct c
    {
        bool operator()(int a, int b) const
        {
            return a < b;
        }
    };
};
int main()
{
    std::vector<int> a1;
    a1.push_back(2);
    a1.push_back(1);
    std::sort(a1.begin(), a1.end(), A::c());

    return 0;
}
Naveen
This answers the question directly but.. it's impossible for it to work correctly this way ;) (you'll break all the points if you sort the vector per single doubles)
RnR
@Naveen: I am assuming that this 'struct Compare' can rest inside of class A. correct?
memC
@RnR, Naveens `operator()` takes pointers to doubles, assuming dynamic arrays.
Georg Fritzsche
I'd make the comparison structs (possibly public) members of the A class. Reduces namespace pollution and creates a clear association, so you can write "std::sort(p.begin(), p.end(), A::Compare_X ());" etc.
Steve314
@RnR: I am assuming double* is actually an array of 3 double values. In that case, sort will work fine.
Naveen
@Naveen : you are right, double* is an array of 3 double values so, p[0] , p[1] and p[2] would give x, y and z respectively. BTW, thanks for the code snippet!!
memC
@Naveen/memC - yup - but if that's the pointer to the 3 doubles then why sort it? ;) (see my reply too)
RnR
+3  A: 

You have two options for sorting: either pass a function/functor to sort or define the operator< for your class. Now, your class A seems to be more of a wrapper for a set of coordinates. So, create another class for your co-ordinates.

 struct Point {
     double x_, y_, z_;
     Point(double x, double y, double z) : x_(x), y_(y), z_(z) {}              
     // just an example, you can refine the following as much as you need
     bool operator<(Point const& other) {
         return x < other.x;
     }
 };  

 bool sortOnY(Point const& l, Point const& r) const {
      return l.y < r.y;
 }

 class A {
     std::vector<Point> pts_;
     void doSomething() {
          sort(pts_.begin(), pts_.end());
     }
     // if sorting on y is also required, you will need 
     // to use a custom comparator which can be either 
     // a functor or a function
     void doSomeOtherThing() {
         sort(pts_.begin(), pts_.end(), sortOnY);
     }
 };         
dirkgently
+1 - But I don't think sorting on x by default is a good idea, vectors don't have a natural order and imposing one can be confusing. It's probably better to just define three functions (`sortOnX/Y/Z` and let the user say explicitly which one he wants.
Manuel
@Manuel: 1) The code posted here tries to touch up on all the possibilities. 2) The `op<` is defined on the points and not on the `vector` -- that is a design choice. 3) The choice of the container is something I'd have commented, but left out, in these cases I'd go for a sorted container than a random access one.
dirkgently
@dirkgently - I meant vector as in `3D vector` sorry for the confusion. I should have said `Point`
Manuel
@Manual: If I am to use functions like sortOnX sortOnY etc, then do I still need to devine a Point class? Would 'bool sortOnX( double * p1, double * p2)' work?
memC
@memC: What do `p1` and `p2` represent? The pointer-to-double makes me worry that you are trying to pass-in an array of 3 doubles. Is that so?
dirkgently
@dirkgently: That is correct .. I the points p1 and p2 is pointer to double... i.e. p[0]= x, p[1] =y, p[2] = z.... looks like this is not the best way and I am better off with what you and others suggested... making a *class Point* ... but now what I want to know is what should operator < look like for this class if I want capability to sort by x or sort by y or sort by Z... or for that I have to implement a method like sortByX etc outside of "point" class?
memC
@memC: The `op<` is limited in the sense that you can use it to sort on only of the coordinates. You'd probably use `op<` for a natural ordering of the points (should such a thing exist, given the problem you are trying to solve) and let functions take care of sorting based on the more exotic criteria.
dirkgently
@memC - it will always be a good idea to use a Point class, it'll make your life so much easier. See the link in my comment to your question for a "state-of-the-art" implementation of a 3d point class.
Manuel
@dirkgently and Manuel -- Thank you guys. You have been of great help as always.
memC
+1  A: 

If you want to sort by x or y or z, those are three different functionalities. Which coordinate to sort by is extra information which doesn't really come from std::sort. You need have an object to pass it on.

struct coord_comparison {
    int coord_id; // <= critical information
    bool operator()( double (*l)[3], double (*r)[3] ) {
        return (*l)[ coord_id ] < (*r)[ coord_id ];
    }
    coord_comparison( int id ) { coord_id = id; }
};

Create this struct inside your class or outside, but it needs to be a structure and not a free function, and operator() cannot be static. Call:

std::sort(points_vec.begin(), points_vec.end(), compare_points( 1 /*for y*/) );

Sorting by all 3 coords at once:

You have

std:vector<double*> points_vec;

I'm going to presume that the double* points to an array of 3 coordinates. This is cleaner:

std:vector<double(*)[3]> points_vec;

std::sort's third argument is a functor which compares two sequence objects:

bool compare_coords( double(*l)[3], double(*r)[3] ) {

Fortunately, comparing two sequences is already coded for you by std::less:

    return std::less( *l, *l + ( sizeof *l/sizeof **l ), r );

(perhaps I did more work than necessary to get the size of the array)

    return std::less( *l, *l + 3, r );
}

This function may be useful outside the class, so I'd make it a free function. You need to make it static if it's going to stay inside the class.

Finally, leave off the parens when passing the function to std::sort:

std::sort(points_vec.begin(), points_vec.end(), compare_points );
Potatoswatter
@Potatoswatter: +1: Thanks for a detailed answer.... and thanks for the note about making it **static**
memC
+1  A: 

I don't think this thread would be complete without a mention of Boost.Bind:

struct Point3D {
    double x, y;
    Point3D(double x=0., double y=0.) : x(x), y(y) {
    }
};

int main() {

    std::vector<Point3D> points;
    points.push_back(Point3D(-1.,  2.));
    points.push_back(Point3D( 2., -1.));
    points.push_back(Point3D(-2.,  0.));

    using boost::bind;
    std::sort(points.begin(), points.end(), 
              bind(&Point3D::x, _1) < bind(&Point3D::x, _2));
    // points sorted by x coord

    std::sort(points.begin(), points.end(), 
              bind(&Point3D::y, _1) < bind(&Point3D::y, _2));
    // points sorted by y coord
}

What a shame std::tr1::bind does not support that. But of course, with a C++0x compiler you'll be able to do this:

std::sort(points.begin(), points.end(), 
          [](Point3D const & a, Point3D const & b) { return a.x < b.x; });
Manuel