comparing things
If you want to sort something, you must be able to
compare two of them.
Write a function:
1 2 3 4
|
bool is_BILL_A_less_than_B_by_amount( const BILL& a, const BILL& b )
{
return a.amount < b.amount;
}
|
Now you can use std::sort():
1 2
|
std::deque <BILL> bills;
std::sort( bills.begin(), bills.end(), is_BILL_A_less_than_B_by_amount );
|
You can even use an inline
lambda (instead of having to make a separate function):
1 2 3 4
|
std::sort( bills.begin(), bills.end(), []( const BILL& a, const BILL& b ) -> bool
{
return a.amount < b.amount;
} );
|
If the function is the most common way you'd want to sort a bill, you can make it the default less-than comparison operator:
1 2 3 4
|
bool operator < ( const BILL& a, const BILL& b )
{
return a.amount < b.amount;
}
|
Then you would only have to provide a comparison function to std::sort() if you are sorting with some different criterion:
1 2
|
// Sort by amount
std::sort( bills.begin(), bills.end() );
|
1 2 3 4 5
|
// Sort by category
std::sort( bills.begin(), bills.end(), []( const BILL& a, const BILL& b ) -> bool
{
return a.category < b.category;
} );
|
std::deque
By the way, you'll have noticed that the bills are in a std::deque. Load all the bills from file into a std::deque (or std::vector), then sort them, then write them back to file.
It helps if you have a function to read a bill from file. And for that, you need to have a file format. Here's an easy CSV file format:
99, sub_name, 99, C, 1.0, 2.0, 3.0, 4.0, 5.0, 99.99 |
Input functions are always a little scary looking, because they have to deal with mistakes in the input data.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
// Read a CSV BILL record
std::istream& operator >> ( std::istream& ins, BILL& bill )
{
// read a line (the entire record)
std::string s;
std::getline( ins, s );
std::istringstream ss( s );
// makes sure that a comma comes next
auto comma = [ &ss, &ins ]() -> void
{
ss >> std::ws;
if (ss.peek() != ',') ins.setstate( std::ios::failbit );
};
// read the BILL fields
ss >> bill.sub_no; comma(); // [spaces] int [spaces] ','
ss >> std::ws; ss.getline( bill.sub_name, 88, ',' ); // [spaces] text ','
ss >> bill.unit; comma(); // [spaces] int [spaces] ','
ss >> std::ws >> bill.c; comma(); // [spaces] char [spaces] ','
for (unsigned n = 0; n < 5; n++)
{ ss >> bill.slab[n]; comma(); } // [spaces] float [spaces] ',' (*5)
ss >> bill.amount >> std::ws; // [spaces] float [spaces] [end]
// If something went wrong above (we haven't reached the end of the line),
// then set the input stream to a fail state
if (!ss.eof()) ins.setstate( std::ios::failbit );
return ins;
}
|
You'll also want an insertion operator to write a bill to file:
1 2 3 4 5 6 7 8 9 10 11 12
|
// Write a CSV BILL record
std::ostream& operator << ( std::ostream& outs, const BILL& bill )
{
outs << bill.sub_no << ", ";
outs << bill.sub_name << ", ";
outs << bill.unit << ", ";
outs << bill.category << ", ";
for (unsigned n = 0; n < 5; n++)
outs << bill.slab[n] << ", ";
outs << bill.amount << std::endl;
return outs;
}
|
Now life is easy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
std::deque <BILL> bills;
// Read all bills from file
{
std::ifstream fin( "CONSUMER.DAT" );
BILL bill;
while (fin >> bill)
bills.push_back( bill );
}
std::sort( bills.begin(), bills.end() );
// Write bills to file
{
std::ofstream fout( "temp.DAT" );
for (const auto& bill : bills)
fout << bill;
}
|
important points
Compare two things with a custom function.
Use that function to sort lists of things.
When manipulating files:
1 - load the entire file into memory (in a std::deque),
2 - do stuff to it in memory,
3 - then write everything back out to file.
Hope this helps.
Caveat: I typed all this in off the top of my head. (So I may have a typo in there.)