2

I want to sort two arrays a and b of the same size in the following way: Array b is ordered the same way array a is being sorted. Example input:

a = {3, 1, 5}
b = {2, 6, 4}

Example out

a = {1, 3, 5}
b = {6, 2, 4}

Such that the values of b are irrelevant for their reordering, but instead follow the reordering of array a.

I want to use stl::sort to do this, and i need to do it as efficiently as possible. So I don't want to copy all elements into structs or order an array with the index which i afterwards could use to order the arrays a and b. What i thought would be the least overhead should be a RandomAccessIterator (since sort requires random access). Now my problem is, I'm really not that good in C++. If someone could give me hints on a level a noob could understand i'd be delighted. I've found some solutions:

Sorting two corresponding arrays, but both proposed solutions don't seem performant enough,

http://www.stanford.edu/~dgleich/notebook/2006/03/sorting_two_arrays_simultaneou.html which uses boost stuff which I assume breaks the compliance with stl (also honestly I don't understand all the template stuff used there, i have a double and an int array and the size n of both, so i don't think i need templates), and finally this

http://www.c-plusplus.de/forum/313698-full solution where i got stuck at the point that i don't know how to implement operator-> and operator* since i don't know if i can return only one value (the value from array a), i.e. if this value is used only for comparison or also for assigning values. also the solution in this thread compares the pointer values for the comparison operators, which i'm not sure is correct (shouldn't it be the values behind the pointers?).

Here's what i have so far, if you see horrible noob mistakes pls tell me where i went wrong :)

#include "DoubleArrayRAccIterator.h"


DoubleArrayRAccIterator::~DoubleArrayRAccIterator() {
    // TODO Auto-generated destructor stub
}
struct doubleValue{
    double* a_val;
    int* b_val;
};

double::DoubleArrayRAccIterator::DoubleArrayRAccIterator(double& a_arr,int& b_arr, int size) {
    a_begin = &a_arr;
    b_begin = &b_arr;
    a = &a_arr;
    b = & b_arr;
    n = size;
}

DoubleArrayRAccIterator::DoubleArrayRAccIterator() {
    a = 0;
    b = 0;
    n = 0;
}

DoubleArrayRAccIterator::DoubleArrayRAccIterator(const DoubleArrayRAccIterator& it) {
    a = it.a;
    b = it.b;
    n = it.n;
}

DoubleArrayRAccIterator& DoubleArrayRAccIterator::operator=(const DoubleArrayRAccIterator& it) {
    a = it.a;
    b = it.b;
    n = it.n;
    return *this;
}

DoubleArrayRAccIterator& DoubleArrayRAccIterator::operator++() {
    ++a;
    ++b;
    return *this;
}


DoubleArrayRAccIterator& DoubleArrayRAccIterator::operator--() {
    --a;
    --b;
    return *this;
}

DoubleArrayRAccIterator DoubleArrayRAccIterator::operator++(int) {
    DoubleArrayRAccIterator it(*this);
    ++a;
    ++b;
    return it;
}


DoubleArrayRAccIterator DoubleArrayRAccIterator::operator--(int) {
    --a;
    --b;
    return *this;
}

DoubleArrayRAccIterator& DoubleArrayRAccIterator::operator+=(diff_type x) {
    a += x;
    b += x;
    return *this;
}


DoubleArrayRAccIterator& DoubleArrayRAccIterator::operator-=(diff_type x) {
    a += x;
    b += x;
    return *this;
}

DoubleArrayRAccIterator DoubleArrayRAccIterator::operator+(diff_type x) const {
    a += x;
    b += x;
    return *this;
}


typename DoubleArrayRAccIterator DoubleArrayRAccIterator::operator-(diff_type x) const {
    a -= x;
    b -= x;
    return *this;
}


DoubleArrayRAccIterator DoubleArrayRAccIterator::operator+(const DoubleArrayRAccIterator& it) const {
    a += it.a;
    b += it.b;
    return *this;
}


DoubleArrayRAccIterator DoubleArrayRAccIterator::operator-(const DoubleArrayRAccIterator& it) const {
    a -= it.a;
    b -= it.b;
    return *this;
}


DoubleArrayRAccIterator::reference DoubleArrayRAccIterator::operator*() const {
    // this MUST be wrong, only return value of array a?
    doubleValue result;
    result.a_val=a;
    result.b_val=b;
    return *result;
}

DoubleArrayRAccIterator::pointer DoubleArrayRAccIterator::operator->() const {
    // this MUST be wrong, only return value of array a?
    doubleValue result;
    result.a_val=a;
    result.b_val=b;
    return &result;
}


DoubleArrayRAccIterator::reference DoubleArrayRAccIterator::operator[](diff_type x) const {
    // this MUST be wrong, only return value of array a?
    doubleValue result;
    result.a_val=a_begin+x;
    result.b_val=b_begin+x;
    return *result;
}


bool DoubleArrayRAccIterator::operator==(const DoubleArrayRAccIterator& it) const {
    return a == it.a;
    //compare indices or values here?
}


bool DoubleArrayRAccIterator::operator!=(const DoubleArrayRAccIterator& it) const {
    return a != it.a;
    //compare indices or values here?
}


bool DoubleArrayRAccIterator::operator<(const DoubleArrayRAccIterator& it) const {
    //compare indices or values here?
}


bool DoubleArrayRAccIterator::operator>(const DoubleArrayRAccIterator& it) const {
    //compare indices or values here?
}


bool DoubleArrayRAccIterator::operator<=(const DoubleArrayRAccIterator& it) const {
    //compare indices or values here?
}


bool DoubleArrayRAccIterator::operator>=(const DoubleArrayRAccIterator& it) const {
    //compare indices or values here?
}


DoubleArrayRAccIterator begin() {
    return DoubleArrayRAccIterator(a_begin, b_begin, n);
}


DoubleArrayRAccIterator end() {
    return DoubleArrayRAccIterator(a_begin + n, b_begin + n, n);
}

And if anyone didn't stop reading yet and still has patience, i'm confused by the "diff_type", "reference" and "pointer" types i just copied from here http://www.c-plusplus.de/forum/313698-full, what are they supposed to be?

Community
  • 1
  • 1
the_toast
  • 175
  • 1
  • 7
  • plain pointers *are* random access iterators. you might just use them. – Arne Mertz Apr 15 '13 at 14:59
  • Hi Arne, thanks for your reply. I want to pass the iterator to the sort function, and in order for both arrays to be sorted i need to iterate over both arrays at the same time. – the_toast Apr 15 '13 at 15:13
  • I see, that wasn't so clear to me at firts reading of your question - just forget my comment please ;) – Arne Mertz Apr 15 '13 at 15:17
  • You might want to have a look at boost.operators, especially the iterator helpers they provide - they will define most of those operators, typedefs and so on for you. wrt the dereferencing operators: yes, you should (and can) return only a pointer to one of the two elements. You could templatize your iterator class to let the operators return either pointers to the first or to the second element. – Arne Mertz Apr 15 '13 at 15:23
  • I'm having a hard time understanding this sentence - *"Array b is ordered the same way array b is being sorted"*. Can you clarify what you mean? – JBentley Apr 15 '13 at 15:40
  • Perhaps you could edit your question with an example of how your two arrays might look to begin with, and how they would look afterwards? – JBentley Apr 15 '13 at 15:41
  • @JBentley Sorry for the confusing explanation, i've added an example (and took out an - even more confusing - error mixing up a and b) – the_toast Apr 15 '13 at 18:38

2 Answers2

4

If I understand you correctly you have this case:

[a1][a2][a3]....[an]
[b1][b2][b3]....[bn]

You want to sort by array a, both of these arrays in the same way, so the result is:

[ax1][ax2][ax3]....[axn]
[bx1][bx2][bx3]....[bxn]

Where xi are indeces same for both arrays.

Consider this example:

class SIter {
public:


   SIter(int* aIter, double* bIter) : aIter(aIter), bIter(bIter) {}

  // we move to next position is just moving both:
  SIter& operator ++() {
     ++aIter; ++bIter;
     return *this;
  }
  SIter operator ++(int) {
     SIter rv = *this;
     ++aIter; ++bIter;
     return rv;
  }
  SIter& operator --() {
     --aIter; --bIter;
     return *this;
  }
  SIter operator --(int) {
     SIter rv = *this;
     --aIter; --bIter;
     return rv;
  }
  SIter operator + (std::ptrdiff_t cc) const
  {
      SIter rv = *this;
      rv.aIter += cc;
      rv.bIter += cc;
      return rv;
  }
  SIter operator - (std::ptrdiff_t cc) const
  {
      SIter rv = *this;
      rv.aIter -= cc;
      rv.bIter -= cc;
      return rv;
  }
  std::ptrdiff_t operator - (SIter other) const
  {
      return aIter - other.aIter;
  }
   struct value_type {
      int a; double b;
      bool operator < (const value_type& other) const {
          return a < other.a; // this is the place where way of sorting is defined!!!!
      }
   };
  struct reference {
     int* a;
     double* b;
     reference& operator = (const value_type& o) 
     {
       *a = o.a;
       *b = o.b;
       return *this;
     }
     operator value_type() const {
         value_type rv = { *a, *b };
         return rv;
     }
     reference& operator = (const reference& other)
     {
        *a = *other.a;
        *b = *other.b;
        return *this;
     }
     bool operator < (const reference& other) const {
        return *a < *other.a; 
     }
     bool operator < (const value_type& other) const {
        return *a < other.a; 
     }
  };

  reference operator * () {
     reference rv = { aIter, bIter };
     return rv;
  }
  bool operator == (const SIter& other) const
  {
      return aIter == other.aIter; // don't need to compare bIter - shall be in sync
  }
  bool operator != (const SIter& other) const
  {
      return aIter != other.aIter; // don't need to compare bIter - shall be in sync
  }
  bool operator < (const SIter& other) const
  {
      return aIter < other.aIter; // don't need to compare bIter - shall be in sync
  }
  // I bet you don't need pointer operator -> ()
   typedef std::random_access_iterator_tag iterator_category;
   typedef std::ptrdiff_t difference_type;
   typedef reference pointer;


private:
  int* aIter; 
  double* bIter;
};

Then use like this:

int main() {
   int a[100] = {};
   double b[100] = {};

   SIter beginIter(a, b);
   SIter endIter(a + 100, b + 100);

   std::sort(beginIter, endIter);

}

I did not try this and probably it has some drawbacks, but you should go in this direction. Your iterator shall consists of two pointers to your arrays (in more general way - iterators to your containers) and your reference type shall shall have operator = and operator < which will be able to assign elements of your both arrays and compare only elements of one array.

[UPDATE]

Good news: I did the working example: ideone

PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
  • For arrays of more general types than just int and double, a performance improvement would be to define swap(const reference &, const reference &) and use swap on the two members, thus avoiding the copying of expensive objects. – Michel Nov 09 '18 at 23:46
0

I'm a little confused by your question, but since you linked to this question, I assume you're trying to solve the same problem as that i.e. you want to sort array a and you want the indices of array b rearranged in the same way they were for a.

This sounds to me like it could be an XY Problem. If at all possible, redesign your code so that you do not have parallel arrays. Instead, place each element of a with its corresponding element of b in a class or std::pair, put those in a collection such as an array or vector, and sort that (possibly overloading the comparison operators, if required).

Community
  • 1
  • 1
JBentley
  • 6,099
  • 5
  • 37
  • 72
  • Thanks for your answer! I agree that this would be the most clean solution, unfortunately it is unfeasible for my problem as it would mean changing lots and lots of code as these arrays are used in multiple locations. Decades of legacy code ;) – the_toast Apr 15 '13 at 18:41
  • I don't know if i could possibly interpret the corresponding elements of the arrays as a pair via "union" to avoid changing the data which would also solve my problem, but my C++ is not good enough to estimate that. My guess would be it's not possible since the two corresponding elements are not in adjacent memory locations. The sorting is already a hotspot in the system, therefore i want to avoid doing any additional operations. – the_toast Apr 15 '13 at 18:55