Searching a linked list, Different data types - c++

I've been tasked with making a program that searches 400+ movies (linked together using a linked list) by title, genre, year, rating, lead actor, etc.
There is a catch though, we are only allowed ONE search function to preform the searches through the linked list. Also, within that search function, we are only allowed one while loop—which I assume in my case will be something like...
while (moviePtr != NULL)
Obviously their will be many different instances if its an actor search, or a genre search. In the case of actor, genre, rating, year, sub-genre, and supporting actor, it should output every single instance of it was found. (for example, if Kevin Bacon was in x-men and the notebook, it should output both not just one of them (to the output file not the screen)).
I've found myself completely stumped by these restrictions that we were given.
How will my search function handle different data types? (year and rating have to be declared as integers). How will it know what exactly I'm searching for? If I'm searching for actor, I don't want it to search for title as well.
Any suggestions on how to get started and get going are very appreciated.
EDIT:
Hi all thought id update you guys on what ive done. I have 3 different search functions. One for the numerical values (the year and the rating), 1 for the genre and actors, and lastly one for the title.
Here is the code for all three of them.
First off title seach.
void TitleSearched(MovieNode*head,
string titleSearched,
ofstream& outFile)
{
MovieNode* moviePtr;
bool found;
moviePtr = head;
found = false;
while (moviePtr !=NULL & !found)
{
if (moviePtr-> title == titleSearched)
{
found = true;
}
else
{
moviePtr = moviePtr -> next;
}
}
if (found)
{
cout << endl << titleSearched << " has been found!\n";
TitleOutput (moviePtr,outFile);
}
else
{
cout << endl << titleSearched << " was not found.\n";
}
}
now the year/ rating search.
int NumSearched(MovieNode* head, int numSearched)
{
int instances;
MovieNode* moviePtr;
ofstream outFile;
moviePtr = head;
instances = 0;
while (moviePtr !=NULL)
{
if (moviePtr-> year == numSearched)
{
instances = instances +1;
NumOutList(moviePtr,outFile,"year",numSearched,instances);
moviePtr = moviePtr -> next;
}
else if (moviePtr->rating == numSearched)
{
instances = instances +1;
NumOutList(moviePtr,outFile,"rating",numSearched,instances);
moviePtr = moviePtr -> next;
}
else
{
moviePtr = moviePtr ->next;
}
}
return instances;
}
lastly the genre/actors search.
int ItemSearch (MovieNode* head,string itemSearched, ofstream& outFile)
{
int instances;
MovieNode* moviePtr;
moviePtr = head;
instances = 0;
while (moviePtr !=NULL)
{
if (moviePtr-> genre == itemSearched || moviePtr ->subGenre == itemSearched)
{
instances = instances +1;
OutList(moviePtr,outFile,"Genre",itemSearched,instances);
moviePtr = moviePtr -> next;
}
else if (moviePtr->leadActor == itemSearched || moviePtr->supportActor == itemSearched)
{
instances = instances +1;
OutList(moviePtr,outFile,"Actor",itemSearched,instances);
moviePtr = moviePtr -> next;
}
else
{
moviePtr = moviePtr ->next;
}
}
return instances;
}
I wanted to remind you guys what my task was.
1. Combine these three search functions into one
2.have only ONE while loop in the search
3. only one return in any given function (however, id assume this would be a void function when combined)
My main issue i beileve is my ints and strings. I am not allowed to declare rating or year as strings. And just the format of the code in combing all three in general is giving me a head ache

You could write your search function in a way which accepts a predicate as a parameter. A predicate is some kind of "functionoid" (meaning, anything which has the ability to be "called" like a function - it could be a function, or a lambda, or a function object..)
in the C++ standard library, predicates are used for many of the standard algorithms, so it's common that you'll see code (using standard containers) like this:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cctype>
bool begins_with_s(std::string s)
{
return s.length() > 0 &&
std::toupper( s.at(0) ) == 'S';
}
bool contains_a_number(std::string s)
{
return std::find_if(s.begin(), s.end(), std::isdigit) != s.end();
}
int main()
{
std::string movies_array[] =
{
"King Kong",
"Singin in the Rain",
"Die Hard 2",
"Superman",
"Star Wars",
"Jaws 3"
};
std::vector<std::string> movies( std::begin(movies_array),
std::end(movies_array) );
// Use predicate - count if the movie name begins with "S"
std::cout << "Movies beginning with S: "
<< std::count_if(movies.begin(), movies.end(), begins_with_s)
<< std::endl;
// Use predicate - count if the movie name contains a number
std::cout << "Movies containing a number: "
<< std::count_if(movies.begin(), movies.end(), contains_a_number)
<< std::endl;
}
The way that C++ standard algorithms are implemented in this way is to accept a template argument representing the predicate, along the lines of
template< typename PredicateType >
void my_function(PredicateType predicate)
{
Movie my_movie;
predicate(my_movie);
}
It's a technique from the functional programming school of thinking - passing a function to a function (Treating a function as a "first class citizen").

You could have your search function take a "match" function as one its parameters, and call this match function on every movie to see whether the movie matches. You can then call your search function with different match functions.
Something like this:
template <typename MatchFunction>
void search_movies(movie* moviePtr, MatchFunction match)
{
while (moviePtr != NULL)
{
if (match(*moviePtr))
{
// output movie
}
moviePtr = moviePtr->next;
}
}
You could then declare match functions like this:
bool matches_actor(movie& m, const std::string& actor)
{
return m.actor == actor;
}
and call it with specific queries like this:
search_movies(moviePtr, std::bind(matches_actor, _1, "Morgan Freeman"));
(std::bind is a C++11 function from <functional>; you could equivalently use boost::bind or std::bind2nd)
Alternately, if you prefer a more C-style way of doing things, you could do something like this:
void search_movies(movie* moviePtr, bool (*match)(movie*, void*), void* match_arg)
{
while (moviePtr != NULL)
{
if (match(moviePtr, match_arg))
{
// output movie
}
moviePtr = moviePtr->next;
}
}
...
bool matches_actor(movie* m, void* actor)
{
return m.actor == *((std::string*)actor);
}
...
std::string actor = "Morgan Freeman";
search_movies(moviePtr, &matches_actor, (void*)(&actor));

Besides the option of passing a functor to check the match, there are other options. One such option would be taking a set of optional conditions to check (you can use boost::optional or a handcrafted approach, or use pointers. For example:
void print_matching( node* list, int * year, std::string * actor... ) {
// iterate over the list:
while (...) {
if ( ( !year || ptr->year == *year )
&& ( !actor || ptr->actor == *actor )
&& ...
)
{
// Film matches, print it
}
}
}
To simplify the function signature you can create a search_pattern type that encapsulates the fields that you want to test (example using a different approach: bools to determine optionality):
struct pattern {
bool check_actor;
std::string actor;
bool check_year;
int year;
};
void print_matching( node* list, pattern const & p ) {
// iterate over the list:
while (...) {
if ( ( !p.check_year || ptr->year == p.year )
&& ( !p.check_actor || ptr->actor == p.actor )
&& ...
)
{
// Film matches, print it
}
}
}
In this last case, you can actually move the test into the pattern object, and have a function:
bool pattern::matches( movie cosnt& m ) const {
return (!check_year || m.year == year )
&&(!check_actor || m.actor == actor );
}
void print_matching( node* list, pattern const & p ) {
// iterate over the list:
while (...) {
if ( p.matches( list->data ) )
{
// Film matches, print it
}
}
}

Related

I want to combine the list and find(), but I don't know how to merge them

Please see the part where the find() function is called. (I ran it in Visual Studio.)
Code:
using namespace std;
int main(void) {
//Implementing the find()
/*
bool Find(const Stu & a); {
return (*it).name;
}
*/
list<Astu>::iterator that;
//that = astu.begin();
//that = find(astu.begin(), astu.end(), (*it).name);
for (it = stu.begin(); it != stu.end(); it++) {
that = find_if(astu.begin(), astu.end(), (*it).name);
if (that != astu.end()) {
all = astu.erase(all);
all++;
}
else
all++;
}
/*
//Filter absenteeism from the total student roster
for (it = stu.begin(); it != stu.end(); it++) {
for (all = astu.begin(); all != astu.end();) {
if (!strcmp((*all).name, (*it).name)) {
//Delete attendees and latecomers from the list
all = astu.erase(all);
}
else
all++;
}
}
*/
cout << "---------------------\n결석자: " << endl;
//이름순으로 정렬
astu.sort(SizeComp2);
//결석자들 출력
for (all = astu.begin(); all != astu.end(); all++) {
cout << "이름: " << (*all).name << endl;
}
return 0;
}
Output:
C2064 error occurred: Term does not evaluate to a function that takes
1 argument.
Even with find_if() in this code, there is a problem. The bool() part in the comment was used for the find_if object, but it doesn't seem to be used well here.
I deleted the part where there was no problem as a result of debugging. You must use an unconditional list, not a vector.
Where should I fix it?
The third argument to std::find_if is a function.
You could use a lambda as the function:
auto that = find_if(astu.begin(), astu.end(), [it](Astu const& astu)
{
return astu.name == it->name;
});
[This assumes that Astu::name is a std::string]

LinkedList sorting a-z

Requirements for this function: If the full name (both the first and last name) is not equal to any full name currently in the list then add it and return true. Elements should be added according to their last name. Elements with the same last name should be added according to
their first names. Otherwise, make no change to the list and return false (indicating that the name is already in the list).
//this function add nodes to the list
//return true if fullname isn't in the list. Else return false if fullname is in the list.
//should be added according to last name.
bool OnlineDating::makeMatch(const std::string& firstName, const std::string& lastName, const OnlineType& value)
{
Node* p = head;
//are these nodes already set to firstName and lastName in this function
Node first;
Node last;
Node* temp = nullptr;
//if the list is empty just insert the fullname and value to the list
if (p == nullptr) {
//add values to the empty list
insertToRear(firstName, lastName, value);
return true;
}
else {
// so this loop is to check if fullname is in the list but first sort in alphebetial order
//sure its added in alphebetical order
//traverse the list after knowing where head is
while (p != nullptr) {
//checking to make sure theres at least another node in the list
if (p->next != nullptr) {
//its not going through ig loop?
//these are used to check and alphebetically selected names
if (p->last > p->next->last) {
insertToRear(p->first, p->last, p->value);
p->next = temp;
return true;
}
else if (p->next->last > p->last) {
insertToRear(p->first, p->last, p->value);
p->next = temp;
return true;
}
//check if full name is already in the list
if (p->last == p->next->last) {
insertToRear(p->first, p->last, p->value);
p->next = temp;
return true;
}
else if (p->first > p->next->first) {
insertToRear(p->first, p->last, p->value);
p->next = temp;
return true;
}
else {
//returns false if it passes through these checks
return false;
}
}
p = p->next;
}
}
}
Here is my main.cpp
int main()
{
OnlineDating clippersGonnaClip;
clippersGonnaClip.makeMatch("Kawhi", "Leonard", 2);
clippersGonnaClip.makeMatch("Paul", "George", 13);
clippersGonnaClip.makeMatch("Ivica", "Zubac", 40);
clippersGonnaClip.makeMatch("Reggie", "Jackson", 1);
clippersGonnaClip.makeMatch("Patrick", "Beverley", 21);
for (int n = 0; n < clippersGonnaClip.howManyMatches(); n++) {
string first;
string last;
int val;
clippersGonnaClip.confirmMatch(n, first, last, val);
cout << first << " " << last << " " << val << endl;
}
return 0;
}
honestly, I just want to create a ptr that will point to each node. Check as long as that node isn't pointing to nullptr, and sort the LinkedList in alphabetical order. finally link the nodes together using my *temp. Why won't it let me go through the if statements every time I compile I get a negative number? Every other function works for this program except this makeMatch(...).
Nothing in the requirements says anything about the data types you need to use. In this case using std::set makes most sense.
By providing a comparison operation on persons and a set you can guarantee uniqueness. Like this :
#include <set>
#include <string>
struct person_t
{
std::string name;
std::string last_name;
unsigned int age;
};
bool operator<(const person_t& lhs, const person_t& rhs)
{
if (lhs.last_name == rhs.last_name)
return lhs.name < rhs.name;
return lhs.last_name < rhs.last_name;
}
int main()
{
// set will ensure all persons are unique
std::set<person_t> persons;
persons.insert({ "Kawhi", "Leonard", 2 });
persons.insert({ "Paul", "George", 13 });
persons.insert({ "Ivica", "Zubac", 40 });
persons.insert({ "Reggie", "Jackson", 1 });
persons.insert({ "Patrick", "Beverley", 21 });
}

C++ - Insertion in a Linked List without using a node's constructor. Is it possible?

I'm working on implementing a Templated Linked List in C++ that will be used to simulate a train moving through numerous stops where traincars are both added and removed. Traincar is its own class and each object is supposed to be given a unique ID starting with 1 and incremented when a car is added. However, when running my code, the id is being incremented more than it is supposed to.
After some experimentation and with help from previous answers, I have determined that it is the new node statements within my LinkedList class methods that are causing the id to be incremented more than wanted. However, I do not see a way to implement insertion methods without creating a new node. Is there any way around this?
Here is my TrainCar class:
class TrainCar {
public:
static int nextID;
int id;
char typeOfCar;
int numberOfStops;
node<char>* car;
TrainCar();
};
int TrainCar::nextID = 1;
TrainCar::TrainCar() {
cout << "id++" << endl;
id = nextID++;
int i = (rand() % 3);//gives a random number 0 - 2, used to determine what
//type of car to add
if(i == 0) {
typeOfCar = 'P';
}
else if(i == 1) {
typeOfCar = 'C';
}
else {
typeOfCar = 'M';
}
car = new node<char>(typeOfCar);
numberOfStops = (rand() % 5) + 1;//gives a random number 1 - 5;
}
Here is my main() function
int main() {
LinkedList<TrainCar> train;
int addCargoCar = 0;
for(int i = 0; i < 10; i++) {
TrainCar newCar;
if(newCar.typeOfCar == 'P') {
train.AddToFront(newCar);
addCargoCar++;
}
else if(newCar.typeOfCar == 'C') {
train.AddAtIndex(newCar, addCargoCar);
}
else {
train.AddToEnd(newCar);
}
}
cout <<"Welcome to the Train Station! Here is your train!" << endl;
char type;
int id, numberOfStops, i, j;
for(i = 0; i < train.size; i++) {
type = train.Retrieve(i).typeOfCar;
id = train.Retrieve(i).id;
numberOfStops = train.Retrieve(i).numberOfStops;
cout << "[" << id << ":" << type << ":" << numberOfStops << "] ";
}
}
The output should be something similar to
[5:P:1][6:P:4][8:P:2][3:P:2][10:C:3][2:C:3][4:C:1][1:M:1][7:M:3][9:M:2]
But my output is:
[17:P:2][9:P:2][5:C:2][19:C:1][15:C:2][1:M:5][3:M:4][7:M:1][11:M:3][13:M:1]
Edit: Here is the AddToFront() method: (all other add methods are similar in nature). The issue with the output is the new node<T>(d) statements
template <class T>
void LinkedList<T>::AddToFront(T d) {
node<T>* newNode = new node<T>(d);
if(head == NULL) {
head = newNode;
tail = newNode;
size++;
}
else {
newNode->next = head;
head = newNode;
size++;
}
}
Edit2: Here is my Retrieve function (now fixed, it no longer uses a new node statement):
template <class T>
T LinkedList<T>::Retrieve(int index) {
node<T>* cur = head;
for(int i = 0; i < index; i++) {
cur = cur->next;
}
return(cur->data);
}
You have the right idea to use a static member variable to keep track of identifiers. But you can't use only that.
The static member variable is a member of the class and not any specific object. Therefore all object share the same id.
Use a static member to keep track of the next possible id, and then use a non-static member variable to store the actual id for the object.
Something like
class TrainCar {
public:
static int next_id; // Used to get the id for the next object
int id; // The objects own id
...
};
TrainCar::TrainCar() {
id = next_id++; // Get next id and save it
...
}
You should probably also have a copy-constructor and copy-assignment operator, otherwise you could get two objects with the same id.
Regarding
Why are the id values so high and why are they being incremented by more than one each time?
That's because you probably create more objects than you expect. With the code you show, as well as with the change suggested above, you will create a new id for every object that is default-constructed. And depending on what your LinkedList template class is doing (why don't you use std::vector) there might be new objects created.
An educated guess is that the Retreive function of your list class default constructs the object it contain. That's why you get three objects constructed when printing, as you call Retrieve three times. Probably a similar story about your Add functions.

Declaring my member function parameters/arguments

class Seller
{
private:
float salestotal; // run total of sales in dollars
int lapTopSold; // running total of lap top computers sold
int deskTopSold; // running total of desk top computers sold
int tabletSold; // running total of tablet computers sold
string name; // name of the seller
Seller::Seller(string newname)
{
name = newname;
salestotal = 0.0;
lapTopSold = 0;
deskTopSold = 0;
tabletSold = 0;
}
bool Seller::SellerHasName ( string nameToSearch )
{
if(name == nameToSearch)
return true;
else
return false;
}
class SellerList
{
private:
int num; // current number of salespeople in the list
Seller salespeople[MAX_SELLERS];
public:
// default constructor to make an empty list
SellerList()
{
num = 0;
}
// member functions
// If a salesperson with thisname is in the SellerList, this
// function returns the associated index; otherwise, return NOT_FOUND.
// Params: in
int Find ( string thisName );
void Add(string sellerName);
void Output(string sellerName);
};
int SellerList::Find(string thisName)
{
for(int i = 0; i < MAX_SELLERS; i++)
if(salespeople[i].SellerHasName(thisName))
return i;
return NOT_FOUND;
}
// Add a salesperson to the salespeople list IF the list is not full
// and if the list doesn't already contain the same name.
void SellerList::Add(string sellerName)
{
Seller(sellerName);
num++;
}
I have some issues with the parameters in my functions in my SellerList class. I want to add someone to the salespeople array so I have a record of all my sellers... Bob, Pam, Tim, etc... My constructor Seller(sellerName) creates a Seller with name sellerName.
How do I add this Seller to the Salespeople array and have capability of a way to pull the data back out and use it in more functions such as a Update function, or an output function?
MAX_SELLERS = 10.... I guess my issue is not knowing whether to use parameters of only
Add(string) or Add(Seller, string). Any help would be appreciated.
Not reinvent the wheel. Choose the container appropiate to your problem. In this case, because you are referencing/searching Sellers by a std::string, I suggest you to use a hash table like std::unordered_map (Or std::map search tree if you don't have access to C++11):
int main()
{
std::unordered_map<Seller> sellers;
//Add example:
sellers["seller name string here"] = /* put a seller here */;
//Search example:
std::unordered_map<Seller>::iterator it_result = sellers.find( "seller name string here" );
if( it_result != std::end( sellers ) )
std::cout << "Seller found!" << std::endl;
else
std::cout << "Seller not found :(" << std::endl;
}
How about using STD vector inside of SellerList instead of the array.
vector<Seller> x;
you can do x.push_back(Seller(...)) or x[0].SellerHasName() and x.size() will give you the number of sellers.
maybe something like this?
// Add a salesperson to the salespeople list IF the list is not full
// and if the list doesn't already contain the same name.
void SellerList::Add(string sellerName)
{
if(num < MAX_SELLERS)
salespeople[num++] = new Seller(sellerName);
}

Missing template arguments before '.' token

I am trying to organize my program into functions and have ran into this,
error: "missing template arguments before '.' token"
once I try to run the code in the function, it works fine if its just in main(). Anyone familiar with this error know what the issue may be?
Note, the commented out code removes the error but messes with the ordered list class and resets its length or something, causing the orderedlist.getlength() function to return 0, which makes none of the code in the while() loop execute.
function:
void rentFilm(char* filmId, char* custId, char* rentDate, char* dueDate, int numFilm)
{
//orderedList <filmType> orderedList(numFilm);
//filmType newItem;
int index = 0;
bool found = false;
while (index < orderedList.getLength() && !found)
{
cout << "test" << endl;
if (strncmp(filmId,orderedList.getAt(index).number,6) == 0 && strncmp("0000",orderedList.getAt(index).rent_id,5) == 0)//If that film is rented by NO customer
{
cout << "test" << endl;
found = true;//customer can rent it
strcpy(newItem.number,filmId);
orderedList.retrieve(newItem);
orderedList.remove(newItem);
strcpy(newItem.rent_id,custId);
strcpy(newItem.rent_date,rentDate);
strcpy(newItem.return_date,dueDate);
orderedList.insert(newItem);
cout << "Rent confirmed!" << endl;
}
else
{
if (strncmp(filmId,orderedList.getAt(index).number,6) > 0 || strncmp("0000",orderedList.getAt(index).rent_id,5) > 0)
{
++ index;
}
else
{
throw string ("Not in list");
}
}
}
}
Insert in orderedList class (where length is determined):
template <class elemType>
void orderedList<elemType>::insert(const elemType& newItem)
{
int index = length - 1;
bool found = false;
if (length == MAX_LIST)
throw string ("List full - no insertion");
// index of rear is current value of length
while (! found && index >= 0)
if (newItem < list[index])
{
list[index + 1] = list [index]; // move item down
--index;
}
else
found = true;
list [index + 1] = newItem; // insert new item
++length;
}
code in main where list is filled:
filmFile.open("films.txt", ios::in);
filmFile >> numFilm;
filmFile.get();
orderedList <filmType> orderedList(numFilm);
filmType newItem;
readString(filmFile, newItem.number,5);
for (int i = 0; i < numFilm; i++)
{
newItem.copy = filmFile.get();
readString(filmFile, newItem.title,30);
readString(filmFile, newItem.rent_id,4);
readString(filmFile, newItem.rent_date,8);
readString(filmFile, newItem.return_date,8);
filmFile.get();
orderedList.insert (newItem);//puts filmType struct into the ordered list.
readString(filmFile, newItem.number,5);
}
Please let me know if code from anywhere else in the program would be helpful in assessing this error.
It looks like the line you commented out declares a variable with the same name as a class.
So when you comment it out, static functions of that class are getting invoked.
Change the declaration to something like:
orderedList<filmType> filmList(numFilm);
and then change all the references of orderedList in the function to filmList.
Is the problem that you are creating a variable with the same name as the template? When you say,
orderedList<filmType> orderedList(numFilm);
that's (sort of) like saying,
int int=42;
and then expecting int+1 to return 43
Try something like,
orderedList<filmType> ol(numFilm);
And changin all the other references to orderedList, to ol.
It seems that you populate a variable orderedList in main() and then expect it to be automatically available in rentFilm(...) when you declare with the same name; that is not possible. You have to pass the object to the function from main() or better to make that function as member method of the class orderedList:
int main ()
{
orderedList<filmType> ol(numFilm); // variable name different (good practice)
... // all the populating
orderedList.rentFilm(...); // call the function like this
}
where, rentFilem() is now part of the class
class orderedList {
...
public:
void rentFilm(char* filmId, char* custId, char* rentDate, char* dueDate, int numFilm);
};
Now inside the function, you don't have to declare variable for orderedList; just use this-><method/variable>. It should work.