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();
}
Related
I am trying to implement insertion of a word into a chained hashtable.
The problem is I am trying to insert a object that has 2 fileds and I need access to one with an iterator. The problem seems to happen with the iterator it as the code doesn't work from the for cycle. I also overloaded the operator== in Vocabolo.cpp to make it work for my case.
I also have a problem the size of the vector, can I use a define? It seems not. Any advices please?
I declared my vector of list + iterator in the header file as :
vector<list<Vocabolo>> hash;
list<Vocabolo>::iterator it;
this is part of the class Vocabolo :
class Vocabolo {
public:
Vocabolo();
~Vocabolo();
void setVocabolo(Vocabolo);
string getVocabolo();
bool operator== (Vocabolo);
string termine;
string tipo;
};
this is the overloaded method operator==:
bool Vocabolo::operator== (Vocabolo x) {
return getVocabolo() == x.termine;
}
the method that is not working!
bool HashV::Insert(Vocabolo nuovo) {
key = this->HashUniversale(nuovo.termine);
for (it = this->hash[key].begin(); it != this->hash[key].end(); it++)
if (it->termine == nuovo.termine)
return false;
else {
hash[key].push_back(nuovo);
return true;
}
}
Consider using std::find_if instead:
auto itVoca = std::find_if(this->hash[key].begin(), this->hash[key].end(), [nuovo](const string& str)
{
return str != nuovo.termine;
});
bool found = itVoca != this->hash[key].end();
if(found ) hash[key].push_back(nuovo);
return found;
Can anybody help me with an iterator problem? I'm having something like this:
class SomeClass{
public:
//Constructor assigns to m_RefPtr a new t_Records
//Destructor deletes m_RefPtr or decreases m_RefCnt
//Copy Constructor assigns m_RefPtr to new obj, increases m_RefCnt
bool Search(const string &);
private:
//Some private variables
struct t_Records{ //For reference counting
int m_RefCnt; //Reference counter
typedef vector<int> m_Vec;
typedef map<string, m_Vec> m_Map;
m_Map m_RecMap;
t_Records(void){
m_RefCnt = 1;
}
};
t_Records * m_RefPtr;
};
//Searchs through the map of m_RefPtr, returns true if found
bool SomeClass::Search(const string & keyword){
//How to create and use an iterator of m_Map???
return true;
}
As how I mentioned, I'm having troubles with creating (defining) map iterator outside of the struct. The map is initalized and contains some records. Thanks for your reply.
Like this:
// assuming m_RefPtr is properly initialized:
t_Records::m_Map::iterator it = m_RefPtr->m_RecMap.begin();
++it; // etc.
By the way, m_Map is a bad name for a type. By common convention, names prefixed with m_ are used for data members.
You can iterate like this
for (m_Map::iterator it = m_RecMap.begin(); it != m_RecMap.end(); ++it)
{
// do stuff with *it
}
Or even easier
for (auto it = m_RecMap.begin(); it != m_RecMap.end(); ++it)
{
// do stuff with *it
}
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.
I have a function that returns an iterator if an object is found.
Now i have a problem. How do i fix the problem of informing the object that called this function that the object was not found?
vector<obj>::iterator Find(int id, int test)
{
vector<obj>::iterator it;
aClass class;
for(it = class.vecCont.begin(); it != class.vecCont.end(); ++it)
{
if(found object) //currently in psuedo code
return it;
}
return ???? // <<< if not found what to insert here?
}
Do i need to change my data structure in this instead?
Thanks in advance! :)
Return vector::end(), throw an exception, or return something other than a plain iterator
Better yet, don't implement your own Find function. That is what the <algorithm> library is for. Based on your psudocode, you can probably use std::find or std::find_if. find_if is particularly useful in cases where equality doesn't necessarily mean operator==. In those cases, you can use a [C++11] lambda or if C++11 isn't available to you, a functor class.
Since the functor is the lowest common denominator, I'll start with that:
#include <cstdlib>
#include <string>
#include <algorithm>
#include <vector>
#include <functional>
using namespace std;
class Person
{
public:
Person(const string& name, unsigned age) : name_(name), age_(age) {};
string name_;
unsigned age_;
};
class match_name : public unary_function <bool, string>
{
public:
match_name(const string& rhs) : name_(rhs) {};
bool operator()(const Person& rhs) const
{
return rhs.name_ == name_;
}
private:
string name_;
};
#include <iostream>
int main()
{
vector<Person> people;
people.push_back(Person("Hellen Keller", 99));
people.push_back(Person("John Doe", 42));
/** C++03 **/
vector<Person>::const_iterator found_person = std::find_if( people.begin(), people.end(), match_name("John Doe"));
if( found_person == people.end() )
cout << "Not FOund";
else
cout << found_person->name_ << " is " << found_person->age_;
}
found_person now points to the person whose name is "John Doe", or else points to people_.end() if that person wasn't found.
A C++11 lambda is new language syntax that makes this process of declaring/defining a functor and using is somewhat simpler for many cases. It's done like this:
string target = "John Doe";
vector<Person>::const_iterator found_person = std::find_if(people.begin(), people.end(), [&target](const Person& test) { return it->name_ == target; });
You can return an iterator to the end, i.e. return class.vecCont.end() to indicate that.
How about just returning the end iterator?
Your code becomes:-
vector<obj>::iterator Find(int id, int test)
{
vector<obj>::iterator it;
aClass class;
for(it = class.vecCont.begin(); it != class.vecCont.end(); ++it)
{
if(found object) //currently in psuedo code
break;
}
return it;
}
or just use std::find.
You should return class.vecCont.end() if the object was not found. But #chris is right - this is exactly what std::find is for.
Something like this
std::vector<obj>::iterator pos;
pos = find(coll.begin(),coll.end(), val);
And don't forget to these check for presence of your element or not in the container
if (pos != coll.end())
Don't return an iterator to a hidden container. Return simply what it is that you want, namely a means to access an object if it exists. In this example, I store the objects in the container via pointer. If your objects only exist temporarily, then new one up and copy the object over!
class AClass;
//...some time later
std::vector<AClass*> vecCont; //notice, store pointers in this example!
//..some time later
AClass * findAClass(int id, int test)
{
vector<AClass*>::iterator it;
for(it = class.vecCont.begin(); it != class.vecCont.end(); ++it)
{
if(found object) //currently in psuedo code
return it;
}
return NULL;
}
//later still..
AClass *foundVal = findAClass(1, 0);
if(foundVal)
{
//we found it!
}
else
{
//we didn't find it
}
edit: the intelligent thing to do is to write a comparator for your class and use the std algorithms sort and to find them for you. However, do what you want.
Never emulate std::algorithm functions inside a class. They are free functions for a reason. It usually is enough to expose begin and end member function that return the right iterators (and possibly a boost::iterator_range). If you need to do a fancy find with a functor, expose the functor as well.
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));
}
}