Suggest way to store and sort in c .
input:
01-02-2022
15-01-2022
20-10-2021
25-12-2022
CodePudding user response:
If you want to sort the dates represented as strings, you will have to parse the strings to order the dates chronologically. You can do this in-flight, if you don't need the parsed dates, or you can first parse all dates and then operate on the parsed data.
I'll show an example that does parsing in-flight, splitting the parsing and ordering parts is an exercise for the reader.
void sort_dates(std::vector< std::string >& dates)
{
std::sort(dates.begin(), dates.end(),
[](std::string const& left, std::string const& right)
{
// Parse dates
unsigned int ly, lm, ld;
if (std::sscanf(left.c_str(), "%u-%u-%u", &ld, &lm, &ly) != 3)
throw std::invalid_argument("Date is in invalid format: " left);
unsigned int ry, rm, rd;
if (std::sscanf(right.c_str(), "%u-%u-%u", &rd, &rm, &ry) != 3)
throw std::invalid_argument("Date is in invalid format: " right);
// Compare the dates chronologically: return true if left < right
if (ly < ry)
return true;
else if (ly > ry)
return false;
if (lm < rm)
return true;
else if (lm > rm)
return false;
return ld < rd;
}
);
}
CodePudding user response:
Slightly based off Andrey Semashev 's answer, you could parse and then store the date into a struct with a constructor taking a string input and having a less than operator for sorting, like so:
#include <algorithm> // for std::less
#include <string>
#include <tuple> // for std::tie
#include <vector>
struct date
{
unsigned int y, m, d;
date(const std::string& date_str) : y(0), m(0), d(0)
{
if (std::sscanf(date_str.c_str(), "%u-%u-%u", &d, &m, &y) != 3)
throw std::invalid_argument("Date is in invalid format: " date_str);
}
friend bool operator< (const date& lhs, const date& rhs)
{
// Check years first, then months, then days
return std::tie(lhs.y, lhs.m, lhs.d) < std::tie(rhs.y, rhs.m, rhs.d);
}
};
void sort_dates(std::vector< std::string >& dates)
{
// Requires an implicit constructor.
// A lambda would be simple to write as an alternative.
std::sort(dates.begin(), dates.end(), std::less<date>{});
}
CodePudding user response:
I would probably convert the strings to time_t
's, and sort those. A time_t
lets you represent the time with a single, easy to sort thing. Up to you whether to keep the original string with that time_t
, or just convert back to the required format of string when you need it back.
#include <iostream>
#include <vector>
#include <chrono>
#include <sstream>
#include <iomanip>
class Date {
tm expanded;
time_t compact;
public:
bool operator<(Date const& other) const {
return compact < other.compact;
}
friend std::istream &operator>>(std::istream &is, Date &d) {
is >> std::get_time(&d.expanded, "%d-%m-%Y");
d.compact = mktime(&d.expanded);
return is;
}
friend std::ostream &operator<<(std::ostream &os, Date const &d) {
return os << std::put_time(&d.expanded, "%d-%m-%Y");
}
};
int main() {
std::istringstream input {
R"(01-02-2022
15-01-2022
20-10-2021
25-12-2022)"};
std::vector<Date> dates { std::istream_iterator<Date>(input), {}};
std::sort(dates.begin(), dates.end());
std::copy(dates.begin(), dates.end(), std::ostream_iterator<Date>(std::cout, "\n"));
}
Result:
20-10-2021
15-01-2022
01-02-2022
25-12-2022