In my current project, I have the following type of set:
typedef set<ItemPtr> ItemSet;
where ItemPtr is this class:
class ItemPtr
{
private:
Item *ptr;
public:
ItemPtr(Item *ptr) : ptr(ptr) { }
Item* getPtr() const { return ptr; }
};
And the following sets:
ItemSet bookList;
ItemSet movieList;
ItemSet musicAlbumList;
Which are all sets contained in a class called Library. Each of these sets contain instances of ItemPtr, where each instance of ItemPtr contains a pointer to to an instance of Book, Movie, or MusicAlbum. Each of these are derived classes from a class called Item. An instance of Book, contains an author, title, number of Pages, and a set of keywords that are common to that book. I have a function as such:
const ItemSet* Library::itemsForKeyword(const string& keyword)
{
return NULL; //need to put code in here
}
that needs to return all items from each set that have the parameter in its list of keywords. I am unsure how to iterate through each set, and access it's keywords, then compare them to the parameter of the above function. How can I do such a comparison?
Here is my Item class:
class Item
{
public:
string mTitle;
string mArtist;
Item(const string& title, const string& artist);
Item();
virtual ostream &print(std::ostream &os) const
{
os << "author: \t" << mArtist << endl;
os << "title: \t" << mTitle << endl;
return os;
}
virtual ~Item();
set<string> keywordsList;
void addKeywords(string keyword);
};
This is the addKeywords function:
void Item::addKeywords(string keyword)
{
keywordsList.insert(keyword);
}
And here is as far as I've gotten thus far on writing the function I need:
const ItemSet* Library::itemsForKeyword(const string& keyword)
{
ItemSet temp;
for(it=bookList.begin();it!=bookList.end();it++){
if(it->getPtr()->keywordsList)
}
return &temp;
}
I know that by referencing getPtr with my iterator, it gives me access to keywordsList, but from that point I don't know how to examine the list to compare it to the passed in keyword. My plan is to, after comparing and finding a match, store the instance in temp, then pass back temp with all the items that contained that keyword. Thanks for the help thus far.
In terms of simply iterating, there are several ways to do it:
Before C++11:
const ItemSet* item_set = // ...
for (ItemSet::const_iterator it = item_set->begin(); it != item_set->end(); ++it) {
const ItemPtr item = *it;
// ...
}
After C++11 (using auto):
const ItemSet* item_set = // ...
for (auto it = item_set->cbegin(); it != item_set->cend(); ++it) {
const ItemPtr item = *it;
}
After C++11 (using ranged-for):
const ItemSet* item_set = // ...
for (auto item : *item_set) {
// ...
}
As far as processing each item, you'll need to show us the code for Item as well as some of your own attempts, first.
Use std::set::find to check whether the keyword is present in the set
http://www.cplusplus.com/reference/set/set/find/
Note: all over your post you are talking about finding the keyword in list. It's not a list that you are using. You are using a set.
Related
When I try to access a function through iterator the compile tells that class ha no member with such name.
class Director
{
private:
std::string stdstrName;
public:
Director(std::string name): stdstrName(name){ }
void setName(std::string name) { stdstrName = name; }
std::string getName() { return stdstrName; }
};
int main(){
std::vector<std::shared_ptr<Director>> stdVecDir;
std::shared_ptr<Director> pointer1 = std::make_shared<Director>("Director1");
std::shared_ptr<Director> pointer2 = std::make_shared<Director>("Director2");
std::shared_ptr<Director> pointer3 = std::make_shared<Director>("Director3");
stdVecDir.push_back(pointer1);
stdVecDir.push_back(pointer2);
stdVecDir.push_back(pointer3);
auto it = std::find(stdVecDir.begin(), stdVecDir.end(), [](std::shared_ptr<Director> dir) { return dir->getName() == "Director2"; });
if (it != std::end(stdVecDir))
std::cout << *it->getName(); // compiler complains that getName() is not a member of class Director
}
Why does the compile complains that getName() is not a member of class Director.
You are using std::find(), but it's used with values, not with callables, hence you need to use std::find_if.
And you are using your iterator to access to a member function of an object pointed to by a pointer which is pointed to by an iterator, hence you have two dereferences, not one.
The following is a correction
auto it = std::find_if(stdVecDir.begin(), stdVecDir.end(),
[](std::shared_ptr <Director> dir) { return dir->getName() == "Director2"; });
if (it != std::end(stdVecDir))
std::cout << it->get()->getName(); //or (*it)->getName();
If im not mistaken you need to change it to (*it)-> to access Director from iterator and shared_ptr. With *it-> you are accessing shared_ptr from the iterator.
First of all sorry if I am asking stupid questions, but I am a beginer in c++.
I am writing a system that represents a library and there is a member function of my Library class that is supposed to allow us to remove a book. Now, if the book is loaned by a user, means there is an element in my _usersLoaningMultimap (multimap<UserId,LoanInfo>). How can I find the LoanInfo that I want without knowing the key (UserId)?
bool Library::removeBook(const BookId& bookId){
//how to find my book in my library without knowing who loaned it.
}
Just to make it clearer, my class Library is like that:
class Library {
public:
Library();
void addUser(const UserId&, const string&);
Optional<string>& getUserInfo(const UserId& userId);
void addBook(const BookId& bookId, const string& description);
Optional<string>& getBookInfo(const BookId& bookId);
bool returnBook(const UserId& userId, const BookId& bookId);
void loanBook(const UserId& userId,LoanInfo& loan);
bool removeUser(const UserId& userId);
void getLoansSortedByDate(const UserId,std::vector<LoanInfo>& loanVector);
~Library() {}
private:
map<BookId, string> _bookMap;
map<UserId, string> _userMap;
multimap<UserId, LoanInfo> _usersLoaningMultimap;
};
You have to iterate through the whole map like this :
for(multimap<userId,LoanInfo>::iterator it = _usersLoaningMultimap.begin(); it != _usersLoaningMultimap.end(); it++){
//it->first retrieves key and it->second retrieves value
if(it->second == loan_info_you_are_searching){
//do whatever
}
}
std::multimap doesn't provide any method for value lookup. Your only choice is to read through the multimap looking for a particular value.
You can use std::find_if for that purpose :
using const_ref = std::multimap<UserId, LoanInfo>::const_reference;
std::find_if(_usersLoaningMultimap.begin(), _usersLoaningMultimap.end(),
[&](const_ref a) -> bool {
return a.second == your_loan_info;
});
If you don't like the syntax, you can also make your own function :
using Map = std::multimap<UserId, LoanInfo>;
auto findLoanInfo(const Map& map, const LoanInfo& info) -> Map::iterator {
for (auto it = map.begin(); it != map.end(); ++it) {
if (it->second == info) {
return it;
}
}
return map.end();
}
Suppose I have this class
class Employee {
string name;
vector<Employee> subEmployee;
Employee(string name, vector<Employee> subEmployee) {
this -> name = name;
this -> subEmployee = subEmployee;
}
vector<Employee> getSubEmployee() {
return this -> subEmployee;
}};
and I create several objects and connect them,
Employee yunfei("yunfei",{});
Employee yuqi("yuqi",{});
Employee yuwei("yuwei",{});
Employee aona("aona",{});
Employee shang("shang",{});
yuqi.addNewEmployee(yuwei);
yuqi.addNewEmployee(aona);
yunfei.addSubEmployee(yuqi);
Then I want to check how many subemployee yunfei has, so I use:
yunfei.getSubEmployee();
The result shows vector contains yuqi,
however, when I try to use:
yunfei.getSubEmployee()[0].getSubEmployee();
I just get an empty list, but when I use:
yuqi.getSubEmployee();
I get the correct answer, so I want to know why that happened.
Like most people have mentioned, you want to make use of references to ensure you are always referencing the same employee.
class Employee
{
using EmployeeList = std::vector<std::shared_ptr<Employee>>;
std::string mName;
EmployeeList mSubEmployees;
public:
Employee(const std::string& name, const EmployeeList& subEmployees = {})
: mName(name), mSubEmployees(subEmployees)
{}
const EmployeeList& GetSubEmployees() const { return mSubEmployees; }
const std::string& GetName() const { return mName; }
void AddSubEmployee(const std::shared_ptr<Employee>& employee) { mSubEmployees.push_back(employee); }
}
// I can make these employees first.
auto yuwei = std::make_shared<Employee>("yuwei");
auto aona = std::make_shared<Employee>("aona");
auto shang = std::make_shared<Employee>("shang");
// I can construct these employees using existing employees
auto yuqi = std::make_shared<Employee>("yuqi", { yuwei, aona });
auto yunfei = std::make_shared<Employee>("yunfei",{ yuqi });
// I can add new subs now
yunfei.addSubEmployee(shang);
// Even brand new employees
yunfei.addSubEmployee(new Employee("Aesthete");
These employees only exist int the context where you declared them. All other employees now only hold reference to the others. You could now iterate the employee tree using recursion.
void PrintEmployeeTree(const Employee& e, int level = 1)
{
for (const auto& employee : e->getSubEmployees)
{
std::cout << std::string(level, '-') << employee->GetName() << std::endl;
PrintEmployeeTree(employee, level+1);
}
}
PrintEmployeeTree(yunfei);
Should give you something like this:
-yunfei
--yuwei
---yuki
---aona
--shang
--Aesthete
Try to use references wherever makes sense. Same goes for const. Notice how even the employee name is return by reference. Look at where all the references I have made are, and how many copies they have avoided. This will ensure you're always working with the same set of data.
Bellow I provide the complete code for something really simple which I'm struggling with..
I need to create a map with strings and Objects...
When requested, if the string is inside the map, I need to return a
reference to one object inside the map
When the string is not inside the map, I need to create that object,
with that string and return (as before) the reference to the new
created object
Please check below the two comments I say "ERROR" to see where the problem is.
My questions are:
How can I insert to a map, one object? what is wrong with the line
on the InitObj()?
How can I create return a reference to an object which I've just
created on a map? As seen at the end of getHouse() function
Thanks in advance :)
#include <map>
#include <string>
#include <memory>
class House
{
public:
House(const char* name) : _name(name) {};
~House() {};
std::string getHouseName () { return _name; }
private:
std::string _name;
House(const House& copy)
{
}
House& operator=(const House& assign)
{
}
};
class Obj
{
public:
Obj()
{
InitObj();
}
~Obj() {};
House& getHouse (const char *houseName)
{
std::string name = houseName;
auto i = _myHouseMap.find(name);
//this string doesn't exist on map? then create a new house and add to the map and return the reference to it
if (i == _myHouseMap.end())
{
//create a new house
House h(houseName);
//add to the map
_myHouseMap.insert(std::pair<const std::string, House>(houseName, h));
//return the reference to the house created
return h; //<--- ERROR!!!! need to return the reference!
}
return (i->second);
}
private:
Obj(const Obj& copy);
Obj& operator=(const Obj& assign);
typedef std::map<const std::string, House> myHouseMap;
myHouseMap _myHouseMap;
//the initialization will add one object to my map
void InitObj()
{
House h("apartment");
_myHouseMap.insert(std::pair<const std::string, House>("apartment", h)); //<--- ERROR see reference to function template instantiation 'std::pair<_Ty1,_Ty2>::pair<const char(&)[10],House&>
}
};
int main(void)
{
Obj aaa;
House& myHouse1 = aaa.getHouse ("apartment");
std::cout << "House is " << myHouse1.getHouseName ();
House& myHouse2 = aaa.getHouse ("newHouse"); //here a new house must be created and added to the map
std::cout << "House is " << myHouse2.getHouseName ();
return 0;
}
For your first question, you made House noncopyable (your copy constructor and copy assignment operator are private). The approach you are taking to insert requires you to make a pair first, the construction of which will copy the House you pass in. If you have access to a C++11 compiler, you can still have the value-type of your map be House and just use emplace instead:
void InitObj()
{
_myHouseMap.emplace(std::piecewise_construct,
std::forward_as_tuple("apartment"), //key
std::forward_as_tuple("apartment")); //value
}
If you don't have access to a C++11 compiler, you will have to change the value type to be House* or some equivalent copy-constructible type.
For the second question, std::map::insert (and emplace) return a pair<iterator, bool>. Just take advantage of that:
if (i == _myHouseMap.end())
{
House h(houseName);
// we insert our new house into the map
// insert() will return a pair<iterator, bool>.
// the bool will be true if the insert succeeded - which we know
// it will because we know that this key isn't already in the map
// so we just reassign 'i' to be insert().first, the new iterator
// pointing to the newly inserted element
i = _myHouseMap.insert(std::pair<const std::string, House>(houseName, h)).first;
}
// here i either points to the element that was already in the map
// or the new element that we just inserted. either way,
// we want the same thing
return i->second;
I have a set where i want to find items in it. Right now i have global objects that i am using to store my finds - (ItemSetMap allMusicByBand)
I would like to get away from this and just search the sets directly.
All the cd info are stored in the private section - (ItemSet allCDS;)
here is the library.cpp -
the commented code is where i was doing my search and adding to the global object...
I would like to do the search in the musicByBand function instead..
#include "Library.h"
#include "book.h"
#include "cd.h"
#include "dvd.h"
#include <iostream>
//ItemSetMap allBooksByAuthor; //these are what i am trying to get away from...
ItemSetMap allmoviesByDirector;
ItemSetMap allmoviesByActor;
//ItemSetMap allMusicByBand;
ItemSetMap allMusicByMusician;
const Item* Library::addMusicCD(const string& title, const string& band, const int nSongs)
{
CD* item = new CD(title,band,nSongs);
allCDS.insert(item);
//ItemSetMap::iterator myband = allMusicByBand.find(band);
//if(myband != allMusicByBand.end())
//{
//myband->second->insert(item);
//}
//else{
//ItemSet* obj = new ItemSet();
//obj->insert(item);
//allMusicByBand.insert(make_pair(band, obj));
//}
return item;
}
const ItemSet* Library::musicByBand(const string& band) const
{
return allMusicByBand[author];
}
i hope i was clear enough on what i wanted.
I have tried to iterate through it. I have tried just about everything i can think of..
CD class is a superclass of item class.
Thank you..
An "idiomatic" way to do it might be to use the std::remove_copy_if algorithm . It would look something like this:
class NotMatching {
string bandName_;
public:
NotMatching( const string& band ) : bandName_( band ) {}
bool operator()( const Item& item ) {
return item.bandName() != bandName_;
}
};
const ItemSet musicByBand(const string& band)
{
ItemSet matchingItems;
std::remove_copy_if( allCDS.begin(), allCDS.end(),
insert_iterator< ItemSet >( matchingItems, matchingItems.begin() ),
NotMatching( band ) );
return matchingItems;
}
But to be honest I think Tyler's approach is simpler and clearer.
The simplest way to do it would be this:
const ItemSet* Library::musicByBand(const string& band) const
{
ItemSet* bandMusic = new ItemSet();
for (ItemSet::const_iterator i = allCDs.begin(); i != allCDs.end(); ++i)
{
if ((*i)->getBand() == band) {
bandMusic->insert(*i);
}
}
return itemSet;
}
Although this runs in O(n) time, which doesn't at all take advantage of the fact that you are using a set. These could just as well be in a vector. The way you were doing it before with the "index" sets is actually a faster-performing solution, although it will take somewhat more memory. Plus, presumably the retrieval methods will be called far more frequently than the insertion methods, so it makes sense to do more work on insertion in order to save on work during retrieval. But of course if you do this, you will want the index sets to be private members, not globals.
You should also be very careful about your memory management here. The fact that you are returning a const pointer to an ItemSet from the musicByBand method concerns me. Why can it not be just an ItemSet that you are returning?
This is a sample code which uses the functor with std::find_if algorithm to search for a particular element in the set
struct BandComparison : public std::binary_function<Item*, std::string, bool>
{
public:
bool operator()(Item* pItem, const std::string& bandName) const
{
bool equal = false;
CD* pCD = dynamic_cast<CD*>(pItem);
if(pCD)
{
equal = pCD->getBand() == bandName;
}
return equal;
}
};
void Library::addCD(const std::string &band)
{
//First check whether CD with this band name exists
ItemSet::iterator iter = std::find_if(m_cds.begin(), m_cds.end(), std::bind2nd(BandComparison(), band));
if(iter == m_cds.end())
{
m_cds.insert(new CD(band));
}
}