tags:

views:

285

answers:

7

Hello everyone,

I've created a class and created an array of objects under that class and filled it all up with data. Now i want to sort the entire array by a specific member of that class, how do I do this using the stable_sort() function?

Edit: Ok, i have this right now,

class sortContiner 
{
public:
    double position;
    int key;
    double offsetXposition;
    double offsetYposition;
    int origXposition;
    int origYposition;
};

I've declared the array like this:

sortContiner * sortList = new sortContiner [length];

Now i want want to sort it by the sortList .position member using stable_sort() like this:

stable_sort(sortList, sortList + length, ????);

What is the comparator function supposed to look like?

+4  A: 

You can put the objects in container ( say, std::vector) and write a functor and use the same in stable_sort.

class Functor
{
    public:
     bool operator() (const Data& info1, const Data& info2)
     {
      return info1.AnyMemberOfData>info2.AnyMemberOfData;
     }
};

std::stable_sort(aVector.begin(), aVector.end(),functor);

Edit: Using arrays ( as @sbi and @wrang-wrang suggested):

Data anArray[10];
//Fill anArray
std::stable_sort(anArray, anArray + 10,functor);

OR write operator < for your class and straight way call stable_sort

aJ
Like sbi said - if they're in an array a of size n, you don't need to put them in a vector; you just stable_sort(a,a+n,lessthan)
wrang-wrang
operator() should be const. The you can pass a temporary into stable_sort() rather than having to declare a new variable.
Martin York
Your `Functor` uses the `>` for comparison. It's possible, but counterintuitive...
xtofl
Yes, < is more intuitive :)
aJ
+1  A: 

You must write your own predicate and pass it to the standard algorithm.

Hai
Yes, you *can* write your own predicate -- or you can implement `operator<()`, as aJ suggests.
j_random_hacker
A: 

You can something like this: The code is self explanatory:

class A
{
public:
    A() : m_a(0), m_b(0)
    {
    }
    void setA(int n)
    {
     m_a = n;
    }
    void setB(int n)
    {
     m_b = n;
    }

    int getA() const
    {
     return m_a;
    }


    int getB() const
    {
     return m_b;
    }


private:
    int m_a;
    int m_b;
};

bool compareA(const A& first, const A& second)
{
    return first.getA() < second.getA();
}

bool compareB(const A& first, const A& second)
{
    return first.getB() < second.getB();
}


int _tmain(int argc, _TCHAR* argv[])
{
    A a[10];
    for(int i = 10; i >= 0; --i)
    {
     a[i].setA(i);
     a[i].setB(i);
    }

    //Sort on variable m_a
    std::stable_sort(a, a + 10, compareA);

    //Sort on variable m_b
    std::stable_sort(a, a + 10, compareB);
}
Naveen
Although the code is, indeed, self explanatory, I find it rather big. (I would like to have it self-reading, too :) ) What's with all these accessor functions... are they necessary?
xtofl
I knew that was coming..it's just that I wanted a compilable yet *simple* example..
Naveen
@Naveen: You example would probably gain a lot if you would simply shrink `A` by making all the accessors into one-liners. Even if you prefer the style with lots of vertical space (`<shudder>`), in a small example for the sake of discussion having to scroll is annoying.
sbi
+5  A: 

You need iterators into your array and some sorting criterion. Let's start with the iterators:

You will need a begin iterator and an end iterator. The begin iterator needs to point to the first element, the end iterator needs to point behind the last element. Pointers are perfect iterators. An array is implicitly convertible into a pointer to its first element, so you have a begin iterator. Add the number of elements in the array to it and you have an end iterator. The number of elements in the array can be obtained by dividing the size (number of bytes) of the array by the size of a single element. Putting it all together:

foo array[10];
const std::size_t array_size = sizeof(array)/sizeof(array[0]);
const int* array_begin = array; // implicit conversion
const int* array_end = begin + array_size;

Now you need something for the algorithm to decide which of two given objects of your class is the smaller one. An easy way to do this would be by overloading operator< for your class:

bool operator<(const foo& lhs, const foo& rhs)
{
  // compares using foo's bar
  return lhs.get_bar() < rhs.get_bar();
}

Now you can sort your array:

std::stable_sort( array_begin, array_end );

If the sorting criterion isn't as fix (say, sometimes you want to sort based on foo's bar data, sometimes based on its wrgly data), you can pass different sorting criteria to the sort algorithm as an optional third parameter. Sorting criteria should be function-like entities. That can be functions or function objects. The latter provide inlining, which is why they are usually better. Here's both:

bool compare_by_bar(const food& lhs, const foo& rhs)
{
  return lhs.get_bar() < rhs.get_bar();
}

struct wrgly_comparator {
  bool operator()(const food& lhs, const foo& rhs) const
  {
    return lhs.get_wrgly() < rhs.get_wrgly();
  }
};

This is how you use them:

std::stable_sort( array_begin, array_end, compare_by_bar );
wrgly_comparator wc;
std::stable_sort( array_begin, array_end, wc );

You can also create the comparator on the fly:

std::stable_sort( array_begin, array_end, wrgly_comparator() );

Edit: Here's a few more hints based on your expanded question:

sortContainer * sortList = new sortContiner [length]; will create a dynamic array on the heap. In C++, there is no garbage collection and you are responsible for cleaning up on the heap after yourself (in this case by invoking delete[] sortList;). This is notoriously hard to do for novices and error-prone even for seasoned programmers. There's a very good chance that what you want is an automatic array: sortContainer sortList[length]; on the stack.

The identifier sortContainer tells me that the thing is a container. However, it's the type of the items to be put into the container. Be more careful by picking identifiers. Proper naming goes a long way towards readable and maintainable code.

sbi
Thanks for your answer, it is almost what I'm looking for. Your "compare_by_bar" function is almost exactly what I'm looking for, except I can't quite decipher the foo and bar stuff (I don't want you to do it for me, rather it would be nice if you could explain exactly what each variable/symbol is doing). Also, thanks for the tips, ill remember it. In this case, i need to use heap arrays because "length" can exceed 10 million, haha stackoverflow!
Faken
Well, after playing around a little with that comparator function and tinkering around with it, I think i got it to work. Thanks for your help!
Faken
You forgot to mention that the class in the array needs copy constructors and methods. In this particular example the compiler generated ones will suffice, but for completeness you should add it to the answer.
Mark Ransom
Mark, that's a very good point. Thanks for bringing it up!
sbi
@Faken: If the array needs to be dynamic, why don't you use a `std::vector`?
sbi
+2  A: 
Nikolai N Fetissov
To the point! If you would have added an example of the free function form, I would have upvoted you twice :)
xtofl
+1  A: 

As a complement to the other answers: The predicate can be created on the fly with boost (or tr1) bind. For example, to sort by the "key" member:

std::stable_sort(sortList, sortList + length,
  boost::bind(&sortContiner::key, _1) < boost::bind(&sortContiner::key, _2)
);
Éric Malenfant
xtofl
Oops! Good catch. I just fixed it.
Éric Malenfant
+1  A: 

I see there are already a couple of solutions, but since I typed mine already, I want to post it anyway. This is a little working solution to give you an idea:

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdlib> 
using namespace std;    //i know :-/


//A very useless class
class myClass{          
private:
  int value;
public:
  myClass(int val){value = val;}
  int getValue(){return value;}
};

//a function which defines how to compare my objects
bool myComparsion (myClass m ,myClass n)
{
    return (m.getValue()<n.getValue());
}

int main(){

  vector<myClass> myvector;
  vector<myClass>::iterator it;

  //Filling up the vector with my objects which are initialized with random numbers
  for (int n=0;n<10;++n)
    myvector.push_back(*(new myClass(rand()%100)));

  //looking at my vector 
  cout<<"Before:";
  for (it=myvector.begin(); it!=myvector.end(); ++it)
    cout<< " "<<it->getValue();
  cout << endl;  

  //sorting the vector and looking again      
  stable_sort (myvector.begin(), myvector.end(), myComparsion);    //this is where the magic happens
  cout<<"After:";
  for (it=myvector.begin(); it!=myvector.end(); ++it)
    cout << " " << it->getValue();
  cout << endl;

  //...

  }

The output should be something like this

Before: 83 86 77 15 93 35 86 92 49 21
After: 15 21 35 49 77 83 86 86 92 93
Lucas
What's the point in defining `it` so long before you need it?
sbi
@sbi: Nothing really. I just typed it together really quickly to illustrate how one could use stable_sort(). This is not supposed to be an example of good coding practice.
Lucas
@Lulu: Maybe it's just me, but I think I'll never understand that. If not in an example created specifically to show some novice how to do something really basic - where else would you employ good coding practice?
sbi