Home > Blockchain >  Sorting a 2D Vector by its Date Column
Sorting a 2D Vector by its Date Column

Time:10-28

I am trying to sort and print a 2D vector by its Date, which is stored in a column in the format of DD-MM-YYYY. I have a 'preload' tickets stored as string and would first looped through it to get the tickets by Username.

From there, I would print push back the User's tickets into a 2d vector before sorting and printing them. The error I'm getting after typing username 'kaya' for instance, is: Debug Asertion Failed! Expression: vector subscript out of range Error Message](https://i.stack.imgur.com/qG8C3.png)

Even when I print out the vector without sorting, the results are still weird, so I am not sure whether it is because I am pushing back wrongly, affecting the sorting as well. Here is the weird output I mean:


*Search Ticket by Username
kaya
invalid. Please try again
2       RM40.0  10      15      3       5       2       kaya    15-10-2020      09:30   10:00
6       RM5.0   5       2       3       4       2       kaya    23-06-2022      08:30   09:30

2
RM40.0
10
15
3
5
2
kaya
15-10-2020
09:30
10:00
6
RM5.0
5
2
3
4
2
kaya
23-06-2022
08:30
09:30

C:\Users\Kar Yee\source\repos\DSTR_KyTest\Debug\DSTR_KyTest.exe (process 4044) exited with code 0.
To automatically close the console when debugging stops, enable Tools->Options->Debugging->Automatically close the console when debugging stops.
Press any key to close this window . . .*

My full code and what I have tried so far:

#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm> // for sort()


string defaultTickets[][11] =
//ticket_id, total_amount,total_distance, total_travelTime,departure_station_id,
//arrival_station_id, user_id, user_name, date, estimated_departure_time, estimated_arrival_time
{
{"1", "RM20.0", "20", "30", "1", "5", "1", "bob", "10-10-2022", "08:30", "10:30"},
{"2", "RM40.0", "10", "15", "3", "5", "2", "kaya", "15-10-2020", "09:30", "10:00"},
{"3", "RM25.0", "5", "10", "5", "6", "1", "bob", "10-05-2022", "11:30", "012:30"},
{"5", "RM30.0", "13", "40", "1", "6", "4", "zhen Hou", "23-05-2022", "08:00", "09:00"},
{"6", "RM5.0", "5", "2", "3", "4", "2", "kaya", "23-06-2022", "08:30", "09:30"},
{"7", "RM8.0", "8", "4", "1", "3", "4", "zhen Hou", "20-05-2022", "08:45", "09:00"},
{"8", "RM10.0", "3", "10", "4", "6", "1", "bob", "10-05-2022", "09:00", "10:00"},

};


bool SortDateColumn(const std::vector<std::string>& a1,
    const std::vector<std::string>& a2)
{
    return atoi(a1[8].c_str()) > atoi(a2[8].c_str());  
}


int main() {

    int num_col = 10;
    int num_row = 7;
    string search_user = "";


    // declare 2D vector
    vector< vector<string>> ticketVector(num_row, vector<string>(num_col));  //preset row and columns
    //vector< vector<string>> ticketVector();
    int size = *(&defaultTickets   1) - defaultTickets;

    
    bool valid = false;
    cout << "Search Ticket by Username" << endl;
    cin >> search_user;

    for (int i = 0; i < size; i  )
    {
        if (search_user == defaultTickets[i][7])
        {
            /*cout << "Ticket ID\t Amount Paid\t Distance\t TravelTime \tDeparture_station_id \t Arrival_station_id \tUserID \t Username" << "";
            cout << "\tdate \testimated_departure_time \testimated_arrival_time" << "\n";*/
            for (int x = 0; x < 11; x  )
            {
                cout << defaultTickets[i][x] << "\t";
                
                ticketVector.push_back({ defaultTickets[i][x] });
                valid = true;

            }
            cout << endl;
        }

        if (valid == false)
        {
            cout << "invalid. Please try again" << endl;
        }
        
    }





    //sort(ticketVector[8].begin(), ticketVector[8].end());
    

    /*std::sort (ticketVector.begin(), ticketVector.end(), [](value& a, value& b)->bool { return a.date < b.date; });
    for (auto& x : ticketVector) cout << x.date << endl;
    return 0;*/

     //Sort the data using the first column of each `std::vector<std::string>` as the criteria
    std::sort(ticketVector.begin(), ticketVector.end(), SortDateColumn);

    //print vector
    for (int i = 0;i < ticketVector.size();i  ) {
        for (int j = 0;j < ticketVector[i].size();j  )
            cout << ticketVector[i][j] << "";
        cout << endl;
    }

    
}

CodePudding user response:

Since your dates conform to the format DD-MM-YYYY, they can be lexographically ordered if you just rearrange the characters to YYYYMMDD. However, you typically want to avoid any kind of dynamic allocation or copying (e.g. rebuilding a string every time) as part of a sorting operation.

So, you can achieve this by combining std::string_view with std::tuple. You can use the string_view to acquire the relevant substrings, and put them in the correct order with a tuple. The tuple supports ordering already.

#include <string_view>
#include <tuple>

using std::string_view;
using std::tuple;

typedef tuple<string_view, string_view, string_view> DateView;

DateView YYYYMMDD(const string& s)
{
    if (s.size() != 10 || s[2] != '-' || s[5] != '-')
        return {};

    return DateView{
        string_view(s.c_str()   6, 4),  // YYYY
        string_view(s.c_str()   3, 2),  // MM
        string_view(s.c_str()   0, 2)   // DD
    };
}

// Returns true if date 'a' is less than date 'b'
bool CompareDate(const string& a, const string& b)
{
    return YYYYMMDD(a) < YYYYMMDD(b);
}

Now you have the basis for sorting. Since you want to sort dates in reverse, you can just invert the result of the CompareDate function:

int main()
{
    vector<string> dates = {
        "10-10-2022",
        "15-10-2020",
        "10-05-2022",
        "23-05-2022",
        "23-06-2022",
        "20-05-2022",
        "10-05-2022",
    };

    std::sort(dates.begin(), dates.end(),
        [](const string& a, const string& b) { return !CompareDate(a, b); });

    for (const auto& date : dates)
    {
        cout << date << "\n"; 
    }
}

As for your actual construction of the vector to begin with, it's a bit of a shambles. You are pushing a single element onto ticketsVector whenever you match the name. As a result, you access these vectors out of bounds later on when you assume they hold an entire row.

Instead of copying the tickets into a new vector, why not actually create them as a vector to begin with? You can reference a ticket by its row index instead of copying. You may also want to use enums to identify columns instead of magic hard-coded numbers.

enum Column
{
    kTicketId, kTotalAmount, kTotalDistance, kTotalTravelTime,
    kDepartureStationId, kArrivalStationId, kUserId, kUserName,
    kDate, kEstimatedDepartureTime, kEstimatedArrivalTime
};

vector<vector<string>> defaultTickets =
{
    {"1", "RM20.0", "20", "30", "1", "5", "1", "bob", "10-10-2022", "08:30", "10:30"},
    {"2", "RM40.0", "10", "15", "3", "5", "2", "kaya", "15-10-2020", "09:30", "10:00"},
    {"3", "RM25.0", "5", "10", "5", "6", "1", "bob", "10-05-2022", "11:30", "012:30"},
    {"5", "RM30.0", "13", "40", "1", "6", "4", "zhen Hou", "23-05-2022", "08:00", "09:00"},
    {"6", "RM5.0", "5", "2", "3", "4", "2", "kaya", "23-06-2022", "08:30", "09:30"},
    {"7", "RM8.0", "8", "4", "1", "3", "4", "zhen Hou", "20-05-2022", "08:45", "09:00"},
    {"8", "RM10.0", "3", "10", "4", "6", "1", "bob", "10-05-2022", "09:00", "10:00"},
};

You can then define your sorting to operate on row indices:

// Sort predicate to order by date in reverse
bool SortDateColumn(int a, int b)
{
    return !CompareDate(defaultTickets[a][kDate], defaultTickets[b][kDate]);
}

// Helper to output a ticket
void OutputTicket(int i)
{
    const auto& ticket = defaultTickets.at(i);
    for (const auto& col : ticket)
    {
        cout << col << " ";
    }
    cout << "\n";
}

And here's your program using all of the above, tidied up a bit:

int main()
{
    string search_user;
    cin >> search_user;

    // Get tickets for user
    vector<int> tickets;
    for (int i = 0; i < defaultTickets.size(); i  )
    {
        if (search_user == defaultTickets[i][kUserName])
        {
            tickets.push_back(i);
        }
    }

    if (tickets.empty())
    {
        cout << "invalid. Please try again" << endl;
        return -1;
    }

    // Sort tickets and output results
    std::sort(tickets.begin(), tickets.end(), SortDateColumn);
    for (int t : tickets)
    {
        OutputTicket(t);
    }
}
  • Related