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);
}
}