This is the exercise, from C++ Primer 5th edition:
10.32: Rewrite the bookstore problem from § 1.6 (p. 24) using a vector to hold the transactions and various algorithms to do the processing. Use
sort
with yourcompareIsbn
function from § 10.3.1 (p. 387) to arrange the transactions in order, and then usefind
andaccumulate
to do the sum."
Here's my code:
#include <iostream> #include <vector> #include <algorithm> #include <iterator> #include <numeric> #include "Sales_item.h" bool compareIsbnv2(const Sales_item &sd1, const Sales_item &sd2) { return sd1.isbn() < sd2.isbn(); } int main() { // get input from user std::istream_iterator<Sales_item> item_iter(std::cin), eof; std::ostream_iterator<Sales_item> out_iter(std::cout, "\n"); // make sure we have data to process std::vector<Sales_item> trans(item_iter,eof); if (item_iter == eof) { std::cerr << "no data to process!" << std::endl; } // sorting vector std::sort(trans.begin(), trans.end(), compareIsbnv2); std::cout << "Summing up data..\n"; for (auto& s : trans) std::cout << s << "\n"; std::cout << "\n\n"; //Sales_item sum; for (auto beg = trans.begin(); beg != trans.end(); beg++) { auto it = std::find_if_not(beg, trans.end(), [&](Sales_item& item) {return item.isbn() == beg->isbn(); }); out_iter = std::accumulate(beg, it, Sales_item(beg->isbn())); std::cout << "\n\n"; } return 0; }
For some reason that I didn't quite understand the original compare isbn function didn't work, so I'm not sure if the result I get is correct:
Input:
0-201-12345-X 3 20.00 0-205-12345-X 5 3.00 0-201-12345-X 2 20.00 0-202-12345-X 10 25.00 0-205-12345-X 10 5.00 0-202-12345-X 5 20.00
Output:
0-201-12345-X 5 100 20 0-201-12345-X 2 40 20 0-202-12345-X 15 350 23.3333 0-202-12345-X 5 100 20 0-205-12345-X 15 65 4.33333 0-205-12345-X 10 50 5
Sales_item.h :
#ifndef SALESITEM_H // we're here only if SALESITEM_H has not yet been defined #define SALESITEM_H // Definition of Sales_item class and related functions goes here #include <iostream> #include <string> class Sales_item { // these declarations are explained section 7.2.1, p. 270 // and in chapter 14, pages 557, 558, 561 friend std::istream& operator>>(std::istream&, Sales_item&); friend std::ostream& operator<<(std::ostream&, const Sales_item&); friend bool operator<(const Sales_item&, const Sales_item&); friend bool operator==(const Sales_item&, const Sales_item&); public: // constructors are explained in section 7.1.4, pages 262 - 265 // default constructor needed to initialize members of built-in type Sales_item() = default; Sales_item(const std::string& book) : bookNo(book) { } Sales_item(std::istream& is) { is >> *this; } public: // operations on Sales_item objects // member binary operator: left-hand operand bound to implicit this pointer Sales_item& operator+=(const Sales_item&); // operations on Sales_item objects std::string isbn() const { return bookNo; } double avg_price() const; // private members as before private: std::string bookNo; // implicitly initialized to the empty string unsigned units_sold = 0; // explicitly initialized double revenue = 0.0; }; // used in chapter 10 inline bool compareIsbn(const Sales_item& lhs, const Sales_item& rhs) { return lhs.isbn() == rhs.isbn(); } // nonmember binary operator: must declare a parameter for each operand Sales_item operator+(const Sales_item&, const Sales_item&); inline bool operator==(const Sales_item& lhs, const Sales_item& rhs) { // must be made a friend of Sales_item return lhs.units_sold == rhs.units_sold && lhs.revenue == rhs.revenue && lhs.isbn() == rhs.isbn(); } inline bool operator!=(const Sales_item& lhs, const Sales_item& rhs) { return !(lhs == rhs); // != defined in terms of operator== } // assumes that both objects refer to the same ISBN Sales_item& Sales_item::operator+=(const Sales_item& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } // assumes that both objects refer to the same ISBN Sales_item operator+(const Sales_item& lhs, const Sales_item& rhs) { Sales_item ret(lhs); // copy (|lhs|) into a local object that we'll return ret += rhs; // add in the contents of (|rhs|) return ret; // return (|ret|) by value } std::istream& operator>>(std::istream& in, Sales_item& s) { double price; in >> s.bookNo >> s.units_sold >> price; // check that the inputs succeeded if (in) s.revenue = s.units_sold * price; else s = Sales_item(); // input failed: reset object to default state return in; } std::ostream& operator<<(std::ostream& out, const Sales_item& s) { out << s.isbn() << " " << s.units_sold << " " << s.revenue << " " << s.avg_price(); return out; } double Sales_item::avg_price() const { if (units_sold) return revenue / units_sold; else return 0; } #endif
Bookstore problem from § 1.6 (p. 24) :
We are now ready to solve our original bookstore problem. We need to read a file of sales transactions and produce a report that shows, for each book, the total number of copies sold, the total revenue, and the average sales price. We’ll assume that all the transactions for each ISBN are grouped together in the input. Our program will combine the data for each ISBN in a variable named total. We’ll use a second variable named trans to hold each transaction we read. If trans and total refer to the same ISBN , we’ll update total. Otherwise we’ll print total and reset it using the transaction we just read:
#include <iostream> #include "Sales_item.h" int main() { Sales_item total; // variable to hold data for the next transaction // read the first transaction and ensure that there are data to process if (std::cin >> total) { Sales_item trans; // variable to hold the running sum // read and process the remaining transactions while (std::cin >> trans) { // if we're still processing the same book if (total.isbn() == trans.isbn()) total += trans; // update the running total else { // print results for the previous book std::cout << total << std::endl; total = trans; // total now refers to the next book } } std::cout << total << std::endl; // print the last transaction } else { // no input! warn the user std::cerr << "No data?!" << std::endl; return -1; // indicate failure }
Sales_item
, and in general it's nice if we could copy&paste your code and run it without having to make any changes (like adding the necessary#include
s).\$\endgroup\$#include
for<algorithm>
,<iostream>
,<iterator>
,<numeric>
, and<vector>
. It would be nice if those were added, as well as any other include directives.\$\endgroup\$