1

I have data that has been given in coordinates. I made their classes and the classes are made of this:

Point2D (int x, int y)

Point3D (int z) //#include Point2D.h

Line2D (Point2D pt1, Point2D pt2)

Line3D (Point3D pt1, Point3D pt2)

Now, I am supposed to be able to sort the data according to any filtering criteria (each class is a filtering criterion), any sorting criteria (x, y, z, or their product) and sorting order (either ascending or descending) The filtering criteria specify which set of records to be displayed.

I've created a generic sort algorithm, but I am supposed to be using the sort function from the STL algorithm. I'm pasting my algo below, but can someone simplify it using the std::sort function.

void sort(
   vector<Point2D*>& point2DList,
   vector<Point3D*>& point3DList,
   vector<Line2D*>& line2DList,
   vector<Line3D*>& line3DList,
   string filterCriteria, string sortingCriteria, string sortingOrder)
{
   if (filterCriteria == "Point2D")
   {
      for (int i = 0; i < (int)point2DList.size() - 1; i++)
      {
         int index = i;
         for (int j = i + 1; j < point2DList.size(); j++)
         {
            if (sortingOrder == "ASC")
            {
               if ((sortingCriteria == "x-ordinate" && point2DList[index]->getX() > point2DList[j]->getX()) || 
                  (sortingCriteria == "y-ordinate" && point2DList[index]->getY() > point2DList[j]->getY()))
                  index = j;
            }
            else if (sortingOrder == "DSC")
            {
               if ((sortingCriteria == "x-ordinate" && point2DList[index]->getX() < point2DList[j]->getX()) || 
                  (sortingCriteria == "y-ordinate" && point2DList[index]->getY() < point2DList[j]->getY()))
                  index = j;
            }
         }
         Point2D* ptr = point2DList[i];
         point2DList[i] = point2DList[index];
         point2DList[index] = ptr;
      }
   }
}

I have only pasted the first part of the algo that involves the Point2D objects, the same flow I have applied for the other 3 classes as well.

JeJo
  • 30,635
  • 6
  • 49
  • 88
wakanada
  • 115
  • 1
  • 9
  • 2
    Can you write a function that takes two items and returns true if the first item should come before the second item? This is what you want to pass to `std::sort`. – Ian Gralinski Aug 23 '20 at 05:47

2 Answers2

1

Fist of all to my understanding, you are using a single sort function (non-template) for sorting different data structure. This is not how you make the generic sort function. You need to go for function templates.

Secondly, the vectors of raw-pointers to those data structure does not look appropriate. The std::vector created/ allocates it's underline data dynamically in free storage, meaning you do not need to put each its elements in manually created memory. If that can not be avoided, I would suggest smart pointers rather than raw pointers there.

Thirdly the filterCriteria,sortingOrder, and sortingCriteria could have been enums rather than std::strings.

Following is the modified sort function, which actually only considers your requirement of using std::sort. Also note that I have used, lambda functions for the custom sorting criteria, which you could read more here: What is a lambda expression in C++11?

// enumerations  for case checking!
enum class FilterCriteria { Point2D = 0, Point3D/*, other cases*/ };
enum class SortingCriteria { x_ordinate = 0, y_ordinate };
enum class SortingOrder { ASC = 0, DSC };

void sort(
   std::vector<Point2D*>& point2DList,
   std::vector<Point3D*>& point3DList,
   std::vector<Line2D*>& line2DList,
   std::vector<Line3D*>& line3DList,
   const FilterCriteria filterCriteria,
   const SortingOrder sortingOrder,
   const SortingCriteria sortingCriteria)
{
   const auto xLessCompare = [](const Point2D* lhs, const Point2D* rhs) { return lhs->x < rhs->x; };
   const auto yLessCompare = [](const Point2D* lhs, const Point2D* rhs) { return lhs->y < rhs->y; };
   const auto xGreaterCompare = [](const Point2D* lhs, const Point2D* rhs) { return lhs->x > rhs->x; };
   const auto yGreaterCompare = [](const Point2D* lhs, const Point2D* rhs) { return lhs->y > rhs->y; };

   switch (filterCriteria)
   {
   case FilterCriteria::Point2D:
   {
      if (sortingOrder == SortingOrder::ASC)
      {
         if (sortingCriteria == SortingCriteria::x_ordinate)
            std::sort(point2DList.begin(), point2DList.end(), xGreaterCompare);
         else if (sortingCriteria == SortingCriteria::y_ordinate)
            std::sort(point2DList.begin(), point2DList.end(), yGreaterCompare);
      }
      else if (sortingOrder == SortingOrder::DSC)
      {
         if (sortingCriteria == SortingCriteria::x_ordinate)
            std::sort(point2DList.begin(), point2DList.end(), xLessCompare);
         else if (sortingCriteria == SortingCriteria::y_ordinate)
            std::sort(point2DList.begin(), point2DList.end(), yLessCompare);

      }
      break;
   }
   case FilterCriteria::Point3D: {
      // ... code
      break;
   }
   default: break;
   };
}

See here for an example code onlone

JeJo
  • 30,635
  • 6
  • 49
  • 88
0

I don't have your class definitions, so i made theese, i hope they are like yours.

If you want to use std::sort you have 2 options:

  1. define less ( < ) operator for class ( or structure )
  2. pass lambda, as a less comparator

1 option is used if sorting used many times, or if classes are stored in containers, that requires less operator (like std::set or std::map) 2 option is good if you sort some structer only in one place in whole code and you need it inline, or you have to overload currently specifed less operator

Here is code with both options, hope it helps. If anything makes you struggle, feel free to comment

#include <algorithm>
#include <vector>
#include <iostream>
#include <random>

struct Point2D
{
    int x;
    int y;

    Point2D(const int x, const int y) : x{x}, y{y} {}

    inline friend bool operator<(const Point2D& p1, const Point2D& p2)
    {
        return ( p1.x == p2.x ? p1.y < p2.y : p1.x < p2.x );
    }
};

// print operator
std::ostream& operator<<(std::ostream& o, const Point2D& p)
{
    return o << "( x=" << p.x << ", y=" << p.y << ")";
}

struct Point3D : public Point2D
{
    Point3D(const int x, const int y, const int z) : Point2D{ x,y }, z{z} {}

    int z;
};

// print operator
std::ostream& operator<<(std::ostream& o, const Point3D& p)
{
    return o << "( z=" << p.z << ", x=" << p.x << ", y=" << p.y << ")";
}

// supportive functions for printing
template<typename T>
void print_collection( const std::vector<T>& vec, const char* collection_name )
{
    std::cout << collection_name << ": ";
    for(const auto& var : vec)
        std::cout << var << ", ";

    std::cout << std::endl;
}

template<typename T>
void print_ptr_collection( const std::vector<T>& vec, const char* collection_name )
{
    std::cout << collection_name << ": ";
    for(const auto& var : vec)
        std::cout << *var << ", ";

    std::cout << std::endl;
}

// support functions for random numbers
int rng()
{
    std::random_device engine;
    std::uniform_int_distribution<int> ranges{ -100, 100 };
    return ranges(engine);
}

int main()
{
    std::vector<Point2D> vec1; vec1.reserve(5);
    std::vector<Point3D*> vec2; vec2.reserve(5);

    // fill
    for(int i = 0; i < 5; i++)
    {
        vec1.emplace_back( rng(), rng() );
        vec2.emplace_back( new Point3D{ rng(), rng(), rng() } );
    }

    // print unsorted
    print_collection( vec1, "vec1 unsorted");
    print_ptr_collection( vec2, "vec2 unsorted");

    // sort with std::sort
    std::sort( vec1.begin(), vec1.end() );
    std::sort( vec2.begin(), vec2.end(), [](const Point3D* p1, const Point3D* p2) -> bool {
        return ( p1->z == p2->z ? (*p1) < (*p2) : p1->z < p2->z );
    } );

    // print sorted
    print_collection( vec1, "vec1 sorted" );
    print_ptr_collection( vec2, "vec2 sorted" );

    // cleanup
    for(int i = 0; i < vec2.size(); i++)
    {
        delete vec2[i];
        vec2[i] = nullptr;
    }

}

This is the output:

vec1 unsorted: ( x=3, y=66), ( x=-1, y=-65), ( x=-9, y=-70), ( x=29, y=16), ( x=32, y=56), 
vec2 unsorted: ( z=-51, x=-57, y=-87), ( z=58, x=49, y=43), ( z=-71, x=-63, y=-87), ( z=23, x=-22, y=13), ( z=-57, x=94, y=-7), 
vec1 sorted: ( x=-9, y=-70), ( x=-1, y=-65), ( x=3, y=66), ( x=29, y=16), ( x=32, y=56), 
vec2 sorted: ( z=-71, x=-63, y=-87), ( z=-57, x=94, y=-7), ( z=-51, x=-57, y=-87), ( z=23, x=-22, y=13), ( z=58, x=49, y=43), 

Edit: forgot to add deleteion of pointers at the end :)