Reputation: 49
Hello I was given a project that ask the user if they want to sort a text file in title,author or genre. My problem is if the user chooses to sort it using genre, he will input a genre (like fiction,fantasy) and the program would print out books associated by that genre in a alphabetical order. Kindly give me pointers or solution on how I should proceed. I am a beginner in c++ I'm sorry if my code doesn't make sense.
Starting out with c++, Tony Gaddis, technical
Fundamentals of Database Systems, Elmarsi & Navathe, technical
One Hundred Years of Solitude, Gabriel Garcia Marquez, fiction
Ashes, Kenzo Kitakana, fiction
* The code
#include <iostream>
#include<string>
#include<fstream>
#include<vector>
#include<algorithm>
#include<sstream>
using namespace std;
struct Book
{
string title;
string author;
string genre;
};
void readFile(ifstream&);
string GetFileName();
vector<Book> Books;
bool compareByTitle(Book, Book);
bool compareByAuthor(Book, Book);
bool compareByGenre(Book, Book);
void Option1();
void Option2();
void Option3();
void print(vector<Book>);
//void filtered_genre(vector<Book>, string);
int main()
{
int UserChoice;
string Newgenre;
ifstream books("books.txt");
while(!books.is_open())
{
books.open(GetFileName());
}
cout << "\nThe file is successfully open\n";
readFile(books);
cout << "\n 1. Sort by Title"
<< "\n 2. Sort by Author"
<< "\n 3. Sort by Genre"
<< "\n 4. EXIT\n"
<< "\n Pls enter your choice: ";
do
{
cin >> UserChoice;
if(UserChoice == 1)
{
Option1();
break;
}
else if(UserChoice == 2)
{
Option2();
break;
}
else if(UserChoice == 3)
{
cout << "Pls entere a genre : ";
cin >> Newgenre;
//Option3();
break;
}
else if(UserChoice == 4)
{
cout << "\nExiting....\n";
}
else
{
cout << "\nInvalid Input\n"
<< "\nPls enter your choice again: ";
}
}
while (UserChoice != 4);
books.close();
}
void print(const vector<Book> BookContents)
{
for(int i=0; i<BookContents.size(); i++)
{
cout << "\n\nTitle: " << BookContents[i].title << endl;
cout << "Author: " << BookContents[i].author << endl;
cout << "Genre: " << BookContents[i].genre << endl;
}
}
void readFile(ifstream& file)
{
Book books;
string line;
while(!file.eof())
{
getline(file, line);
stringstream ss(line);
getline(ss, books.title,',');// Extracts title from the file
getline(ss, books.author,',');// Extracts author name from the file
getline(ss, books.genre);// Extracts genre from the file
Books.push_back(books);// Adds books object to vector
}
}
string GetFileName()
{
string FileNameByUser;
cout << "\nThe file you are trying to open doesn't exist\n"
<< "\nPls enter the file again : ";
getline(cin, FileNameByUser);
cout << "\nThe path you entered is : ";
cout << FileNameByUser + "\n";
return FileNameByUser;
}
bool compareByTitle(Book book1, Book book2)
{
return book1.title < book2.title;
}
bool compareByAuthor(Book book1, Book book2)
{
return book1.author < book2.author;
}
bool compareByGenre(Book book1, Book book2)
{
return book1.genre < book2.genre;
}
void Option1()
{
cout << "\nSorting the book contents by title\n";
std::sort(Books.begin(), Books.end(), compareByTitle);
print(Books);
}
void Option2()
{
cout << "\nSorting the book contents by author\n";
std::sort(Books.begin(), Books.end(), compareByAuthor);
print(Books);
}
void Option3()
{
cout << "\nSorting the book contents by genre\n";
std::sort(Books.begin(), Books.end(), compareByGenre);
print(Books);
}
Upvotes: 2
Views: 736
Reputation: 15265
OK, John gave the answer, which is good and has already many upvotes.
Additionally I will show a full modern, object oriented C++ solution, using classes and algorithms.
I would like to especially emphasize that we should overwrite the extractor and inserter operator for classes. Because only the class should know, how to read and write its data. The outside world should not know and take care.
Initially there was only a book class, but I added a also a Books class, to reflect the concept, of having a collection of books.
For this "Books" class, we define the functions necessary needed for user commands.
Please note: In the shown sort functions, I use a pointer to class members, to select, what member shall be used in operations. This makes life easier.
Please have a look a the source code below:
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <regex>
// Remove leading and trailing spaces
std::string trim(const std::string& s) {
return std::regex_replace(s, std::regex("^ +| +$"), "$1");
}
struct Book
{
std::string title;
std::string author;
std::string genre;
// Extractor and Inserter ------------------------------------------------------------------------------
// Overwrite extractor for easier input
friend std::istream& operator >> (std::istream& is, Book& b) {
return std::getline(std::getline(std::getline(is, b.title, ','), b.author, ','), b.genre);
}
// Overwrite inserter for easier output
friend std::ostream& operator << (std::ostream& os, const Book& b) {
return os << "\nTitle:\t " << b.title << "\nAuthor:\t" << b.author << "\nGenre:\t" << b.genre << '\n';
}
};
struct Books {
// All the books
std::vector<Book> data{};
// Extractor and Inserter ------------------------------------------------------------------------------
// Overwrite extractor for easier input
friend std::istream& operator >> (std::istream& is, Books& b) {
b.data.clear();
std::copy(std::istream_iterator<Book>(is), {}, std::back_inserter(b.data));
return is;
}
// Overwrite inserter for easier output
friend std::ostream& operator << (std::ostream& os, const Books& b) {
std::copy(b.data.begin(), b.data.end(), std::ostream_iterator<Book>(os, "\n"));
return os;
}
// -----------------------------------------------------------------------------------------------------
// Sort the books according to given member
void sortBooksAndPrint(std::string Book::*member, std::ostream& os) {
std::sort(data.begin(), data.end(), [&](const Book& b1, const Book& b2) { return b1.*member < b2.*member; });
os << *this;
}
// Filter the books. Filter can also be a part of the string
void filterAndPrintBooks(std::string Book::* member, const std::string filter, std::ostream& os) {
std::sort(data.begin(), data.end(), [&](const Book& b1, const Book& b2) { return b1.title < b2.title; });
std::copy_if(data.begin(), data.end(), std::ostream_iterator<Book>(os), [&](const Book& b) {
return trim(b.*member).find(trim(filter)) != std::string::npos; });
}
};
// Sow menu and get user selection
unsigned int menuOption() {
std::cout << "\n\n 1. Sort by Title\n 2. Sort by Author\n 3. Sort by Genre\n 4. Filter by Title\n"
" 5. Filter by Author\n 6. Filter by Genre\n 7. Show list\n\nPress 0 for Exit.\n\nPlease select: ";
unsigned int option{};
std::cin >> option;
return option;
}
int main() {
// Open file with books and properties
if (std::ifstream booksFileStream{ "r:\\books.txt" }; booksFileStream) {
// Here we will store our books data
Books books{};
// Read complete file
booksFileStream >> books;
// Show menu and get user commands
for (bool doLoop{ true }; doLoop; )
switch(menuOption()) {
case 1:
books.sortBooksAndPrint(&Book::title, std::cout);
break;
case 2:
books.sortBooksAndPrint(&Book::author, std::cout);
break;
case 3:
books.sortBooksAndPrint(&Book::genre, std::cout);
break;
case 4:
std::cout << "\nFilter by title: Please enter title: ";
if (std::string filter{}; std::cin >> filter)
books.filterAndPrintBooks(&Book::title, filter, std::cout);
break;
case 5:
std::cout << "\nFilter by author: Please enter author: ";
if (std::string filter{}; std::cin >> filter)
books.filterAndPrintBooks(&Book::author, filter, std::cout);
break;
case 6:
std::cout << "\nFilter by Genre: Please enter Genre: ";
if (std::string filter{}; std::cin >> filter)
books.filterAndPrintBooks(&Book::genre, filter, std::cout);
break;
case 7:
std::cout << books;
break;
case 0:
doLoop = false;
break;
default:
std::cout << "\nWrong Selection. Please select valid menu option\n";
}
}
else {
std::cerr << "\n*** Error: Cannot open source file with books\n";
}
return 0;
}
If you have questions, then I am happy to answer.
Upvotes: 1
Reputation: 88017
So I think you have all the skill you need to solve this problem. You know how to add items to a vector and you know how to sort a vector using different criteria. The only piece left, as you say in the title, is how to filter the data.
There's lots of different ways to do this, I'm going to suggest a very straightforward one. First add a genre parameter to your Option3 function, so that you can pass the genre that you've read in main to that function.
void Option3(string genre);
int main()
{
...
else if(UserChoice == 3)
{
cout << "Pls entere a genre : ";
cin >> Newgenre;
Option3(Newgenre);
break;
}
...
}
void Option3(string genre)
{
...
}
Now in the function Option3
we're going to do the filtering. There are four simple steps
Declare a new vector to hold the filtered data, call it (say) FilteredBooks
Now write a loop that goes through all the books and compares the book's genre to the genre that the user is interested in. If the genres are the same then add the book to the FilteredBooks
vector.
Now sort the FilteredBooks
vector alphabetically (that's what you said, I guess you mean alphabetically by title).
Now print the FilteredBooks
vector. You already have a function to do this.
That's it. Hope this helps.
Upvotes: 4