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\$