I don't understand why my code doesn't insert to the list.
But if I change the line where I use the insert by doing a push_back the element is inserted to the list. Thank you
But I have to have it sorted. The operator < is well implemented:
void Movies::afegirPeliculaDirector(string director,string title,int year){
list<actorDirectorMovie> llista;
actorDirectorMovie dir(title,year);
int total=_mapDirectors.count(director);
if (total>0){
map<string,list<actorDirectorMovie> >::iterator
it=_mapDirectors.find(director);
llista=(*it).second;
list<actorDirectorMovie>::iterator itList=(*it).second.begin();
while(itList!=(*it).second.end() and (*itList) < dir){
itList++;
}
if (itList==(*it).second.end()) llista.push_back(dir);
else {
cout << llista.size() << endl;
llista.insert(itList,dir);//->>>>>>>>>>>>>>>>>>>> IT DOESN'T INSERT, WHY?
cout << llista.size() << endl;
}
it->second=llista;
}
else {
llista.push_back(dir);
_mapDirectors.insert(make_pair(director,llista));
}
directorMovies(director);
}
Your itList iterator refers to a different container. itList points to a member of _mapDirectors[director].second but you're trying to use it as an insertion point into llista.
I think the problem is that you wanted llista to be a reference to the mapped list, but instead you're making a copy of it.
Try the following code (I haven't tested it but it should give you the idea). Note that it takes a reference to the mapped list. There's also no need to test for an empty map, or do an explicit find for the director - you can simply reference _mapDirectors[director].second and the empty list will be created automatically if it's not already in the map.
void Movies::afegirPeliculaDirector(string director,string title,int year)
{
// get a reference to the director's list
list<actorDirectorMovie>& llista = _mapDirectors[director];
actorDirectorMovie dir(title,year);
list<actorDirectorMovie>::iterator itList=llista.begin();
while(itList!=llista.end() and (*itList) < dir){
itList++;
}
if (itList==llista.end()) llista.push_back(dir);
else {
cout << llista.size() << endl;
llista.insert(itList,dir);
cout << llista.size() << endl;
}
}
You could also consider changing from using a list to store each director's movies to a set, as this has the property of being sorted automatically for you. The whole function would be reduced to _mapDirectors[director].insert(dir) in that case.
Related
I've been trying to write my own implementation of linked list, but the code segfaults when I try to access an the third element or anything after it. Adding elements doesn't segfault, but accessing does. I can't find the pointer error in my get() function.
Each node in the list stores data (of Template t) and a pointer leading to the next node. I have two functions for everything- one for the first element, and one for any subsequent elements. The get() function for the subsequent elements always segfaults. I have some debug messages in the function that spit out results I can't explain. For example, if I run a get() request for the second element, an then the third, the code doesn't segfault, but it does return clearly incorrect results. Debug messages I placed indicate the segfault occurs when the second element calls the function to check the third element, if it occurs at all. Try the code with and without the line cout << newList.get(2) << endl; and you'll get very different results.
One possible cause is the pointer storage- I have the get() function output the pointer of each element (except the first) as it cycles through, and compare them to the pointers outputted by the add() function, and and pointers for element 0 and 1 match, but 2 and beyond do not match, and I can't seem to figure out why that would be.
#include <iostream>
using namespace std;
template <class T> class myLinkedList{
T data;
myLinkedList<T> *next = NULL;
public:
myLinkedList(T input){
data = input;
}
void add(T input){
if(next == NULL){
myLinkedList<T> newItem(input);
next = &newItem;
cout << "adding to list, data is " << input << ", pointer is " << next << endl;
}else{
myLinkedList<T> nextEntry = *next;
nextEntry.add(input);
}
}
T getData(){
return data;
}
//the start of the get function, only used by the first entry in the list
T get(int entry){
int currentPosition = 0;
if(entry == currentPosition){
return getData();
}else{
//defrefrence the pointer anc check the next entry
myLinkedList<T> nextEntry = *next;
return nextEntry.get(entry, ++currentPosition);
}
}
private:
//this vesion is the hidden, private vesion only used by nodes other than the first one
//used to keep track of position in the list
T get(int entry, int currentPosition){
//cout << currentPosition << endl;
if(entry == currentPosition){
return data;
}else{
//derefrence the pointer and check the next entry
cout << next << endl;
myLinkedList<T> nextEntry = *next;
currentPosition++;
T output = nextEntry.get(entry, currentPosition);
return output;
}
}
};
int main(){
myLinkedList<int> newList(3);
newList.add(4);
newList.add(5);
newList.add(7);
newList.add(9);
cout << newList.get(2) << endl;
cout << newList.get(3) << endl;
return 0;
}
Results are clearly erroneous- program should spit oout two macthing sets of pointers, as well as the numbers 5 and 7 ( the list elements)
One of your main problems is here:
if(next == NULL){
myLinkedList<T> newItem(input); // <<<<<<<<<<<<<
next = &newItem;
cout << "adding to list, data is " << input << ", pointer is " << next << endl;
}
you allocate an item on stack inside the if scope. Then you make next to point to this item. But... lifetime of the item is bounded by this scope. As son as you exit the scope, this item does not exist any longer. You need to allocate it dynamically by 'new' or other methods.
I had a breakthrough! Following Serge's solution was helpful, but one more change was needed- rather than create a function reference in the else block of my add function,
eg
myLinkedList<T> nextEntry = *next;
nextEntry.add(input)
i needed to use the pointer directly, as in
next->add(input)
I didn't know my pointer/object syntax
I have the following for each C++ code:
for (auto item : myVector)
{
std::cout << item;
if (item == orderBy.IsLast()) // <--- Check if this is the last element
std::cout << "(Is last element) " << std::endl;
else if (item == orderBy.IsFirst()) // <-- Check if this is the first element
std::cout << "(Is first element)" << std::endl;
}
Of course IfLast() and IfFirst() do not exist on std::vector. Is there a native std:: way to check for first and last element ?
You shouldn't use the range-based for in this case, as this kind of for "hides" the iterator, and you'd need an additional counter to keep track of the position in vector. You can simply do
for(auto it = myVector.begin(); it != myVector.end(); ++it)
{
if(it == myVector.begin()) // first element
{
// do something
}
else if(std::next(it) == myVector.end()) // last element
{
// do something else
}
}
Note that simply comparing my.Vector.back() with your element from a range-based for is OK only if you're sure that you don't have duplicates in the vector. But if e.g. the value of the last element appears multiple times in the vector, you're going to find only its first position. So that's why there's really no good way of using a range-based for without an additional index that keeps track of where exactly in the vector you are.
EDIT See also #thelink2012's answer for how to "trick" your range-based for so you can get the position of the element implicitly.
Use the std::vector::front and std::vector::back to get a reference to the data in the first and last positions.
Reference is a keyword here because you could efficiently check the address of your iterating item and the address of the respective front/back references. In your example you take the item by value not reference so this prehaps wouldn't work, take in consideration this example that'd work with this method:
for(auto& item : myVector) // take item by reference
{
std::cout << item;
if (&item == &myVector.back())
std::cout << "(last element) " << std::endl;
else if (&item == &myVector.front())
std::cout << "(first element)" << std::endl;
}
If the object overloads the address of operator & (though it's considered a bad practice) you might want to use std::addressof instead.
This method won't work however for the std::vector<bool> specialization since it optimizes the vector to store booleans efficiently with bits, and since we cannot have references to bits, all references taken out this data structure is a proxy object not exactly tied to the address of the internal data.
Use std::vector::front() for the first element.
Use std::vector::back() for the last element.
Before you call those functions, make sure that the vector is not empty.
if (!orderBy.empty() && item == orderBy.back()) <--- Check if this is the last element
else if (!orderBy.empty() && item == orderBy.front()) <-- Check if this is the first element
For value based comparison, you may use myVector.front()/myVector[0] as the first and myVector.back()/myVector[myVector.size()-1] as the last element.
Suggestion
Capture the reference by default to avoid unwanted copies. e.g.
for(const auto& I : myVector)
You could search for it again, but that would be rather inefficient. If you need information on the position of the current item, you probably want to use an iterator or an index:
for (std::size_t i=0; i<myVector.size(); ++i)
{
auto& item = myVector[i];
std::cout << item;
if (i == (myVector.size() - 1))
std::cout << "(Is last element) " << std::endl;
else if (i == 0)
std::cout << "(Is first element)" << std::endl;
}
If you have special cases for the boundaries you should use the ol' iterator version, but separating the first and last cases away from the loop.
If the cases share the code after that if you should encapsulate it on a function.
I can't write code from my phone :c
This works for me
vector<int> vi {1,2,3,4};
cout << "vi = {";
for (const auto &e : vi) {
cout << e;
if (&e != &vi.back())
cout << ',';
}
cout << '}' << endl;
I am confused which is more efficient?
As we can access map directly, why do we need to use find?
I just need to know which way is more efficient.
#include <iostream>
#include <map>
using namespace std;
int main ()
{
map<char,int> mymap;
map<char,int>::iterator it;
mymap['a']=50;
mymap['b']=100;
mymap['c']=150;
mymap['d']=200;
//one way
it=mymap.find('b');
cout << (*it).second <<endl;
//another way
cout << mymap['b'] <<endl;
return 0;
}
thanks in advance! :)
Using find means that you don't inadvertently create a new element in the map if the key doesn't exist, and -- more importantly -- this means that you can use find to look up an element if all you have is a constant reference to the map.
That of course means that you should check the return value of find. Typically it goes like this:
void somewhere(const std::map<K, T> & mymap, K const & key)
{
auto it = mymap.find(key);
if (it == mymap.end()) { /* not found! */ }
else { do_something_with(it->second); }
}
As we can access map directly, why do we need to use find?
Because map<>::operator[] is sometimes nasty. If an element doesn't exist then:
it inserts it
value initialize it
returns reference of value
Thus it always returns a valid reference of value, even if a key din't exist previously. This behavior is not intended many times.
On the other hand map<>::find() is safer; because it returns end(), if a value doesn't exit. Another advantage of find() is that it returns an iterator which contains references to key (first) and value(second) both.
The [] operator in map is not constant it is logarithmic. Most of the books stress on this fact and point out it is a bit misleading. So both find and [] operator are with the same complexity.
Please note that the [] operator will create the entry even if it does not exist while find will return end() in that case.
This code and doc is picked from cplusplus.com
// accessing mapped values
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main ()
{
map<char,string> mymap;
mymap['a']="an element";
mymap['b']="another element";
mymap['c']=mymap['b'];
cout << "mymap['a'] is " << mymap['a'] << endl;
cout << "mymap['b'] is " << mymap['b'] << endl;
cout << "mymap['c'] is " << mymap['c'] << endl;
cout << "mymap['d'] is " << mymap['d'] << endl;
cout << "mymap now contains " << (int) mymap.size() << " elements." << endl;
return 0;
}
OP:
mymap['a'] is an element
mymap['b'] is another element
mymap['c'] is another element
mymap['d'] is
mymap now contains 4 elements.
Notice how the last access (to element 'd') inserts a new element in the map with that key and initialized to its default value (an empty string) even though it is accessed only to retrieve its value. Member function map::find does not produce this effect.
I have a Playlist class that has a vector with Tracks and each Track has a multimap<long, Note> as datamember.
class Track {
private:
multimap<long, Note> noteList;
}
Using an iterator to acces the tracks is no problem, so this part here is working fine:
vector<Track>::iterator trackIT;
try{
for(noteIT = trackIT->getNoteList().begin(); noteIT != trackIT->getNoteList().end(); noteIT++){
cout << "---" << noteIT->second.getName() << endl;
}
}catch (int e){
cout << "exception #" << e << endl;
}
What I want to do next is iterate the Notes of each Track. But starting from this part all output is stopped. So I only get to see the first tracks name. Any cout's after that are not shown and the compiler isn't giving me any errors. Even the cout inside the try catch block isn't working..
vector<Track>::iterator trackIT;
multimap<long, Note>::iterator noteIT;
for(trackIT = this->playlist.getTracklist().begin(); trackIT < this->playlist.getTracklist().end(); trackIT++){
cout << trackIT->getTrackName() << endl;
for(noteIT = trackIT->getNoteList().begin(); noteIT != trackIT->getNoteList().end(); noteIT++){
cout << "---" << noteIT->second.getName() << endl;
}
}
cout << "random cout that is NOT shown" << endl; // this part doesn't show up in console either
Also, the method in my Track class that I'm using to add the Note objects looks like this:
void Track::addNote(Note ¬e) {
long key = 1000009;
this->noteList.insert(make_pair(key, note));
}
// I'm adding the notes to the track like this:
Note note1(440, 100, 8, 1, 1);
note1.setName("note1");
synthTrack.addNote(note1);
Any ideas why the iterator won't work?
Change
noteIT < trackIT->getNoteList().end()
To
noteIT != trackIT->getNoteList().end()
Not all iterators support less than / greater than comparisons.
If you have c++11 you can use a range-based for loop:
for (Note& note : trackIT->getNoteList())
Or you can use BOOST_FOREACH
BOOST_FOREACH (Note& note, trackIT->getNoteList())
You haven't shown the definitions of getTrackList or getNoteList, but there's a common mistake people make - if you return a copy of the container instead of a reference to it, the iterators will be pointing to different containers making comparisons impossible. Not only that but since the containers are temporary any use of the iterators results in undefined behavior.
If you are really hardcoding the track key, then there will only ever be one track in the map because std::map stores unique keys...
long key = 1000009; //If yo are really doing this, this key is already inserted so it will fail to insert more.
Also, if you would like a more elegant approach you could use function object.
struct print_track
{
void operator()(const Track& track)
{
cout << track.getTrackName() << endl;
std::for_each(track.getNoteList().begin(), track.getNoteList().end(), print_track_name());
}
};
struct print_note_name
{
void operator()(const std::pair<long,Note>& note_pair)
{
cout << "---" << note_pair.second.getName() << endl;
}
};
//In use...
std::for_each(playlist.getTracklist().begin(), playlist.getTracklist.end(), print_track());
I'm trying to get a simple bit of code to work. I have a function called 'get_object_radius' which searches an area for instances of 'Creature', and pushes their pointers to a vector, then returns the vector.
I then want to cycle through them all and display their names from outside the function. I'm pretty sure I'm adding them to the vector correctly, but I'm not cycling through the vector of pointers correctly, am I?
Here's the relevant code snippet (that doesn't work):
//'get_object_radius' returns a vector of all 'Creatures' within a radius
vector<Creature*> region = get_object_radius(xpos,ypos,radius);
//I want to go through the retrieved vector and displays all the 'Creature' names
for (vector<Creature*>::iterator i = region.begin(); i != region.end(); ++i) {
cout<< region[i]->name << endl;
}
Any ideas what I'm doing wrong?
http://www.cplusplus.com/reference/stl/vector/begin/
You dereference the iterator to get to the underlying object.
cout << (*i)->name << endl;
Try:
//I want to go through the retrieved vector and displays all the 'Creature' names
for (vector<Creature*>::iterator i = region.begin(); i != region.end(); ++i) {
cout << (*i)->name << endl;
}
You need to dereference the iterator (using the * operator), which then gives you Creature* pointer.
To get the element iterator is pointing at, you dereference it (like a pointer, but iterators are not necessarily pointers). So your code should look like this:
// auto is C++11 feature
for (auto it = region.begin(); it != region.end(); ++it) {
Creature *p = *it;
std::cout << p->name << "\n";
}
In C++11 you also get range for, which hides iterators from your view:
for (Creature *p : region) {
std::cout << p->name << "\n";
}