cr1stian
cr1stian

Reputation: 35

How to structure a function that can Sort an Array of Pointers using the pointer members

New CS Student in need of direction/help. I trying to sort my pRecordBook Array by the class member picked on user end. But for some reason when executing my comparison is just voided or not taken.

clang 11.0.3 Xcode

#include <iostream>
#include <fstream>
#include <string>

const int MAX_RECORDS = 200;

class Record{ // Record Class with members
private:
    std::string id;
    std::string name;
    int quantity;
    double price;

public:
    Record();
    const std::string getID();
    const std::string getName();
    const double getPrice();
    const int getQuantity();
    void setID(std::string num);
    void setName(std::string input);
    void setQuantity(int quantity);
    void setPrice(double price);
    void setRecord(std::string id, std::string name, int quantity, double price);

    std::string displayRecord();
};

void readFile(Record recordBook[], Record *pRecordBook[], std::string file, int &count);
void displayArray(Record recordBook[], const int count);
void displayArray(Record *pRecordBook[], const int count);
void sortArray(Record *pRecordBook[], const int count, int selection);
std::string searchRecord(Record *pRecordBook[], const int count, std::string &input);
std::string searchRecord(Record *pRecordBook[], const int count, std::string &input);
void printReport(Record recordBook[], const int count);
void displayOptions(int &choice);

int main(int argc, const char * argv[]) {
    int selection = 0;
    int subSelection = 0;
    std::string inputID = "";
    std::string inputName = "";
    int count = 0;
    Record recordBook[MAX_RECORDS];
    Record *pRecordBook[MAX_RECORDS];
    std::string fileName = "testFile.txt";


    readFile(recordBook, pRecordBook, fileName, count);

    displayOptions(selection);

    while(selection != 0){
        switch(selection){
            case 1:
                std::cout << "\nPrinting Unsorted Inventory";
                displayArray(recordBook, count);
                break;
            case 2:
                std::cout << "\nSort By\n1. ID\n2. Name\n3. Quantity\n4. Price\nSelection: ";
                std::cin >> subSelection;
                while(subSelection < 1 || subSelection > 4){
                    std::cout << "\nPlease a selection from 1-4\nTry Again: ";
                    std::cin >> subSelection;
                }
                sortArray(pRecordBook, count, subSelection);
                displayArray(pRecordBook, count);
                break;
            case 3:
                std::cout << "\nSearch for item by:\n1. ID\n2. Name\nSelection: ";
                std::cin >> subSelection;
                if(subSelection > 2 || subSelection < 1){
                    std::cout << "\nPlease a selection of 1 or 2\nTry Again: ";
                    std::cin >> subSelection;
                }else{
                    if(subSelection == 1){
                        std::cout << "\nEnter ID to search for: ";
                        std::cin >> inputID;
                        searchRecord(pRecordBook, count, inputID);
                    }else{
                        std::cout << "\nEnter the Name to search for: ";
                        std::cin >> inputName;
                        searchRecord(pRecordBook, count, inputName);
                    }
                }
                break;
            case 4:
                printReport(recordBook, count);
                break;
            default:
                std::cout << "\nInvalid Option, Try Again\n";
                break;
        }
        displayOptions(selection);

    }
    if(selection == 0){
        std::cout << "\nTerminated Program. Goodbye\n";
    }

    return 0;
}

// Get Functions
const std::string Record::getID(){ return id;}
const std::string Record::getName(){ return name;}
const double Record::getPrice(){ return price;}
const int Record::getQuantity(){ return quantity;}

// Set Functions
void Record::setID(std::string num){
    this->id = num;
}
void Record::setName(std::string input){
    std::string name;
    for(char letter: input){
        name += toupper(letter);
    }
    this->name = name;
}
void Record::setQuantity(int quantity){
    this->quantity = quantity;
}
void Record::setPrice(double price){
    this->price = price;
}

// Contsructor for the initialization of "recordBook Array"
Record::Record(){
    id = "";
    name = "";
    quantity = NULL;
    price = NULL;
}

// Function to set the Entire class at once - Called in readFile function
void Record::setRecord(std::string id, std::string name, int quantity, double price){
    setID(id);
    setName(name);
    setQuantity(quantity);
    setPrice(price);
}

// Reads file, checks if correct file, checks if its empty, grabs values and stores them in class Record on the recordBook array
void readFile(Record recordBook[], Record *pRecordBook[], std::string fileName, int &count){
    std::ifstream inFile;
    std::ofstream outFile;

    inFile.open(fileName, std::ios::in);
    outFile.open("errorFile.txt", std::ios::out | std::ios::app);

    while(!inFile){
        std::cout << "\nError: Could Not Open File\nTry Again: ";
        std::cin >> fileName;
        inFile.open(fileName, std::ios::in);
    }
    while(inFile.peek() == EOF){// Checking if file is empty
        std::cout << "\nError: File is Empty\nTry Again: ";
        std::cin >> fileName;
        inFile.open(fileName, std::ios::in);
    }

    std::string id;
    std::string name;
    int quantity;
    double price;

    while(inFile >> id >> name >> quantity >> price && !(inFile.eof())){
        if(price == 0 || quantity == 0){
            outFile << id << " " << name << " " << quantity << " " << price << "\n";
        }else{
            recordBook[count].setRecord(id, name, quantity, price);
            pRecordBook[count] = &recordBook[count];
            count++;
            }
        if(count == MAX_RECORDS){
            std::cout << "\nProgram Storage Full. Stopping on line " << MAX_RECORDS << "\nUsing values grabbed. . . ";
            break;
        }
    };
    outFile.close();
    inFile.close();
}

std::string Record::displayRecord(){ // Function to display individual Record
    return this->id + " " + this->name + " " + std::to_string(this->quantity) + " " + std::to_string(this->price);
}

void displayArray(Record recordBook[], const int count){ // Function to display all Records in RecordArray
    for(int i = 0; i < count; i++){
        std::cout << "\nItem: " << (i+1) << " " << recordBook[i].displayRecord();
    }
    std::cout << "\n";
}

void displayArray(Record *pRecordBook[], const int count){ // Function display all Record in PointerArray
    for(int i = 0; i < count; i++){
        std::cout << "\n" << pRecordBook[i]->displayRecord();
    }
    std::cout << "\n";
}

I've worked backwards and even plugged in my regular array, chose one condition, hard coded it in the if statement. - Boom Works

I've then included the switch statement because I had thought the break; in the switch was causing me to get kicked out of my nested loops killing the function in doing so. - Nope works

It's when I have the Pointers Array plugged in that the comparison fails. Is there a cast I am missing or?

Thank you for your time!

// Function to sort array depending on user selection
void sortArray(Record *pRecordBook[], const int count, int selection){

    bool toSwap;
    bool condition;
    Record *pTemp;

    for(int i = 0; i < count; i++){
        toSwap = false;
        for(int j = i + 1; j < count-i-1; j++){

            // Seems like our problem is the sorting is not being saved
            // Possibly grabbing the data incorrectly or might be the sorting itself that is wrong

                 switch(selection){
                    case 1:
                        condition = pRecordBook[j]->getID() < pRecordBook[i]->getID();
                        break;
                    case 2:
                        condition = pRecordBook[j]->getName() < pRecordBook[i]->getName();
                        break;
                    case 3:
                        condition = pRecordBook[j]->getQuantity() < pRecordBook[i]->getQuantity();
                        break;
                    case 4:
                        condition = pRecordBook[j]->getPrice() < pRecordBook[i]->getPrice();
                        break;
                    default:
                        std::cout << "\nError concurred - sorting bv default: Name";
                        condition = pRecordBook[j]->getName() < pRecordBook[i]->getName();
                        break;
                }

            if(condition){
                pTemp = pRecordBook[i];
                pRecordBook[i] = pRecordBook[j];
                pRecordBook[j] = pTemp;
                toSwap = true;
            }
        }
        if(toSwap == false)
            break;
    }
}

std::string searchRecord(Record *pRecordBook[], const int count, std::string id){ // Function searches for ID
    for(int i = 0; i < count; i++){
        if(id == pRecordBook[i]->getID())
            return "\nRecord Found at Index " + std::to_string(i) + ": " + pRecordBook[i]->displayRecord();
        }
    return "\nRecord Not Found!";

};


std::string searchRecord(Record *pRecordBook[], const int count, std::string &input){ // Function searches for Name
    for (int i = 0; i < count; i++) {
        if(input == pRecordBook[i]->getName()){
            return "\nRecord Found at Index " + std::to_string(i) + ": " + pRecordBook[i]->displayRecord();
        }
    }
    return "\nRecord Not Found!";
};


void printReport(Record recordBook[], const int count){ // Prints Inventory Report
    double totalValue = 0.0;

    for(int i = 0; i < count; i++)
        totalValue += recordBook[i].getPrice();

    std::cout << "Report:\nTotal Number of items: " << std::to_string(count) << "\nTotal worth of Inventory: $";
    std::cout << std::setprecision(2) << std::fixed << totalValue;
};

void displayOptions(int &choice){ // Displays Main Menu

    std::cout
    << "\n1: Print Inventory Unsorted"
    << "\n2: Sort in ascending order by any field"
    << "\n3: Search for an item by ID or name"
    << "\n4: Print Report with total count and worth of inventory"
    << "\nChoose an Option: ";
    std::cin >> choice;
}

Upvotes: 1

Views: 45

Answers (1)

Adrian Mole
Adrian Mole

Reputation: 51845

Your sorting logic is flawed!

First, the toSwap check will end the sorting prematurely (in most cases). For example, as soon as the i loop runs and finds no value less than that at the current i index, the search will stop. So, in a list of 3 items, with quantities of (in the unsorted list) 1, 3 and 2, then toSwap will be false at the end of the first loop but the 3 and 2 still need to be swapped.

So, first fix: remove

   if (toSwap == false)
       break;

and, thus, you can remove the toSwap variable completely!

Second, the 'test' condition of you inner (j) loop is really weird! You must run to the end of the list each time.

So, second fix: change

    for(int j = i + 1; j < count-i-1; j++){

to

    for(int j = i + 1; j < count; j++){

I have tested your given code code, with these changes made, on the following input file, and it works, as far as I can tell:

123 Cheese   5 12.30
212 Mutton   1 44.67
202 Chicken  3 12.78
363 Orange   5 6.22
327 Lemon   10 8.13
124 Butter   4  6.45

(I have no idea what your actual data values will be, of course, so I made some up!)

EDIT: Probably a new question, but there is also a problem with your "Search for an item..." options, as the compiler can't properly distinguish calls to the two searchRecord functions. The two calls:

searchRecord(pRecordBook, count, inputID);
searchRecord(pRecordBook, count, inputName);

have exactly the same profile.

Upvotes: 1

Related Questions