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));
}
}
Related
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();
}
I have class called "UltrasoundTemplate". These UltrasoundTemplate objects contain an int parameter, which shows when they where defined (something like a time stamp). And I have a class called "UltrasoundTarget" which contains a vector of UltrasoundTemplate's.
I add UltrasoundTemplates to the vector with push_back(ultrasoundTemplate).
Now I want to sort the vector by the order of time stamps instead of the order I added them to the vector.
I found a lot of answers in google, which all show me the same solution, but obviously I'm still doing something wrong. Here are the code snippets I think are necessary for finding a solution:
ultrasoundTemplate.h
class UltrasoundTemplate
{
public:
UltrasoundTemplate(/*...*/);
int getVolumePos() { return volume_; }
private:
int volume_;
};
ultrasoundTarget.h
//the sort algorithm
struct MyTemplateSort {
bool operator() ( UltrasoundTemplate t1, UltrasoundTemplate t2){
int it1 = t1.getVolumePos();
int it2 = t2.getVolumePos();
if (it1 < it2)
return true;
return false;
}
};
class UltrasoundTarget
{
public:
UltrasoundTarget(/*...*/);
vector<UltrasoundTemplate> getTemplates() { return USTemplateVector_; }
private:
vector<UltrasoundTemplate> USTemplateVector_;
};
FMainWindow.cpp
void FMainWindow::match_slot()
{
int i;
//here I get the name of the target I'm looking for
QTreeWidgetItem *item = targetInfoWidget_->treeWidget->currentItem();
int index = targetInfoWidget_->treeWidget->indexOfTopLevelItem(item);
QString itemToAppendName = item->text(0);
for(i = 0; i < USTargetVector.size(); i++){
if(USTargetVector.at(i).getName() == itemToAppendName) {
//here I try to sort
MyTemplateSort tmpltSrt;
std::sort(USTargetVector.at(i).getTemplates().begin(),
USTargetVector.at(i).getTemplates().end(), tmpltSrt);
break;
}
}
As an example: I define Template1 in Volume(0), Template2 in Volume(70) and Template3 in Volume(40). The order now is (Template1, Template2, Template3) but I want it to be (Template1, Template3, Template2). But this code is not doing it.
If there's Information missing, just tell me and I'll provide more code.
Thanks alot.
Your getTemplates() method returns by value, making a mess here:
std::sort(USTargetVector.at(i).getTemplates().begin(),
USTargetVector.at(i).getTemplates().end(), tmpltSrt);
You are sorting an incompatible iterator range. You can fix that particular problem by returning a reference:
vector<UltrasoundTemplate>& getTemplates() { return USTemplateVector_; }
It is common practice to add a const overload to such a method:
const vector<UltrasoundTemplate>& getTemplates() const { return USTemplateVector_; }
You can also modify your comparison functor to avoid unnecessary copies (and for general readability and const correctness):
struct MyTemplateSort {
bool operator() const ( const UltrasoundTemplate& t1, const UltrasoundTemplate& t2)
{
return t1.getVolumePos() < t2.getVolumePos();
}
};
This will require that you make getVolumePos() a const method, which it should be anyway:
class UltrasoundTemplate
{
public:
...
int getVolumePos() const { return volume_; }
...
};
Note that is is not generally good practice to provide references to the private data of a class. If possible, you should find a way to remove that from the UltraSoundTarget interface. You could, for instance, expose a pair of iterators, and/or give the class a sort method.
juanchopanza answer is correct, the problem is the way you are returning the vector from UltrasoundTarget. Just to touch another topic, maybe it would be nice to change a little the designing of your implementation. As UltrasoundTarget is a container of Ultrasound's, it makes sense to implement the sort as a method of this class, this way you have direct access to USTemplateVector_ and will save unecessary copies. Something like:
class UltrasoundTarget
{
public:
UltrasoundTarget(/*...*/);
vector<UltrasoundTemplate> getTemplates() { return USTemplateVector_; }
void sort();
private:
vector<UltrasoundTemplate> USTemplateVector_;
};
void UltrasoundTarget::sort()
{
std::sort(USTemplateVector_.begin(), USTemplateVector_.end(), tmpltSrt);
}
void FMainWindow::match_slot()
{
int i;
//here I get the name of the target I'm looking for
QTreeWidgetItem *item = targetInfoWidget_->treeWidget->currentItem();
int index = targetInfoWidget_->treeWidget->indexOfTopLevelItem(item);
QString itemToAppendName = item->text(0);
for(i = 0; i < USTargetVector.size(); i++){
if(USTargetVector.at(i).getName() == itemToAppendName)
{
//here I try to sort
MyTemplateSort tmpltSrt;
USTargetVector.at(i).sort();
break;
}
}
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 am working on a piece of code that copies a 'Person' object from one data representation to another. The names in each class (name, address, title) match, and all types are strings. For each field I want to apply the same transformation, based on some condition that also depends on the field name. The tricky part is that the repeating code uses function suffixes that are based on the field name. It looks something like this:
LibraryA::Person person1;
LibraryB::Person person2;
if (person1.name_valid() && [...somestuff...]) {
string *v = SomeOtherFunction(person.name())
person2.set_name(v);
}
if (person1.address_valid() && [...somestuff...]) {
string *v = SomeOtherFunction(person.address())
person2.set_address(v);
}
if (person1.title_valid() && [...somestuff...]) {
string *v = SomeOtherFunction(person.title())
person2.set_title(v);
}
Is there a trick (or technique :) ) to factor out the repetitive part to a template? I'd prefer a solution that does not involve defining a macro (that would be too easy :) )
This fits your requirements, but whether I would use it or not is a different question. Only if there is a huge amount of repetition I would go through this path, and then I would combine it with a macro to simplify the calling code:
void test_and_set( Person const & person1, Person & person2,
bool (Person::*test)() const,
std::string (Person::*get)() const,
void (Person::*set)( std::string const &) )
{
if ( (person1.*test)() ) {
(person2.*set)( (person1.*get)() );
}
}
Used as:
test_and_set( person1, person2, &Person::valid_name, &Person::get_name, &Person::set_name );
And combined with a local macro:
#define TEST_AND_SET( p1, p2, field ) \
test_and_set( (p1), (p2), &Person::valid_##field, &Person::get_##field, &Person::set_##field )
TEST_AND_SET( person1, person2, name );
TEST_AND_SET( person1, person2, title );
#undef TEST_AND_SET
You can use pointer to member function. For example (I didn't check if this code compiles):
typedef bool (LibraryA::Person::Validator)();
typedef string (LibraryA::Person::FieldGetter)();
typedef void (LibraryB::Person::FieldSetter)(string*);
void ApplyField(LibraryA::Person& person1, LibraryB::Person& person2, Validator vl, FieldGetter get, FieldSetter set)
{
if (person1.vl() && [...somestuff...])
{
string* v = SomeOtherFunction(person1.get());
person2.set(v);
}
}
ApplyField(person1, person2, &LibraryA::Person::name_valid, &LibraryA::Person::name, &LibraryB::Person::set_name);
ApplyField(person1, person2, &LibraryA::Person::address_valid, &LibraryA::Person::address, &LibraryB::Person::set_address);
ApplyField(person1, person2, &LibraryA::Person::title_valid, &LibraryA::Person::title, &LibraryB::Person::set_title);
I don't think templates fit here because all fields are of the same type.
And I don't really know what you have against macros in this case. You can use a macrot to generate the call to ApplyField() if you want.
You can use an array of pointer-to-member objects, fill it with the source and destination of the transformation and then apply the transformation to each entry in that array. This could look something like this:
struct trans_info {
trans_info(bool (S::*valid)() const,
std::string* (S::*get)()const,
void (T::*set)(std::string*)):
valid_(valid),
get_(get),
set_(set)
{
}
bool (S::*valid_)() const;
std::string* (S::*get_)() const;
void (S::*set_)(std::string*);
};
trans_info const info[] = {
trans_info(&S::name_valid, &S::name, &T::set_name),
trans_info(&S::address_valid, &S::address, &T::set_address),
trans_info(&S::title_valid, &S::title, &T::set_title),
...
};
template <typename T, int Size> T* begin(T (&array)[Size]) { return array; }
template <typename T, int Size> T* end(T (&array)[Size]) { return array + Size; }
transform(S const& person1, T& person2)
{
for (trans_info const* it(begin(info)), e(end(info)); it != end; ++it)
{
if ((person1.*(it->valid_))() && [...somestuff...]) {
string *v = SomeOtherFunction(person1.*(it->get_))())
(person2.*(it->set))(v);
}
}
}
Did this quick, certainly not valid C++ but I hope you get the idea:
struct MyFunctor
{
Person *person1, *person2;
void operator()(void Person::*validator(), string* Person::*getter(), void Person::*setter(string *))
{
if (person1->*validator() && [...somestuff...])
{
string* v = SomeOtherFunction(person1->*getter());
person2->*setter(v);
}
}
};
// Usage
MyFunctor f = { person1, person2 };
f(&Person::name_valid, &Person::name, &Person::set_name);
f(&Person::address_valid, &Person::address, &Person::set_address);
f(&Person::title_valid, &Person::title, &Person::set_title);
You can use pointer-to-member arguments to a template.
But I question the use of pointers to strings, that looks like a probable memory leak.
If the person objects are immutable to you, you are out of luck.
If they are not, factor the information out of the method name by using tag classes Person::Name, Person::Address and so on, and then re-writing *_valid and *_set to use function overloading:
bool Person::is_valid( Name, std::string ) {...)
bool Person::is_valid( Address, std::string ) {...)