Ticket* Championship::findTicketByFanID(unsigned int id) {
hTabTickets::iterator it = tickets.begin();
while(it != tickets.end()) {
if(it->getOwnerID() == id)
return it;
}
}
Hello there
I'd like to return the object that is on iterator it. tickets is an hash table that holds objects of the type Ticket. When I search for that, there is any solution to return that object?
I want to be able to do this so I can do Ticket t1 = findTicketByFan(id);
hTabTickets:
struct eqTicket {
bool operator() (const Ticket &b1, const Ticket &b2) const{
return b1.getID() == b2.getID();
}
};
struct hTicket{
int operator() (const Ticket &b1) const{
return b1.getID();
}
};
typedef tr1::unordered_set<Bilhete, hTicket, eqTicket> hTabTickets;
Regards
Maybe you want this:
Ticket* Championship::findTicketByFanID(unsigned int id) {
for (hTabTickets::iterator it = tickets.begin(); it != tickets.end(); ++it) {
if(it->getOwnerID() == id)
return &(*it);
}
return NULL;
}
If you search for an id very frequently, you may want to change that set for a map.
References come to a great use here. Typically returning a local pointer is not a good idea. An example for your case is given below. Two problems with your the code: (a) not all paths return value and (b) hash table should locate quickly (ideally O(1)), so looping is a bad idea. Further, it is not being incremented in the loop.
class Campeonato {
// Using STL hashmap
map<unsigned int, int> tickets;
public:
// the return value tells you whether to use iterator or not.
bool findTicketByFanID(unsigned int id, map<unsigned int, int>::iterator &it) {
// std::map provides method find
it = tickets.find(id);
if (it != tickets.end())
return true;
return false;
}
};
I want to be able to do this so I can do Ticket t1 = findTicketByFan(id);
If it is your intention, return type is not correct, it should return by value or (const) reference, not pointer. Another issue, you use std::unordered::set and try to search in a loop, but you should use std::unordered_set::find() instead:
Ticket Championship::findTicketByFanID(unsigned int id)
{
hTabTickets::iterator it = tickets.find( Ticket( id ) );
if( it == tickets.end() ) // not found do something
throw std::runtime_error( "Ticket not found" );
return *it;
}
if creating temporary ticket is too expensive, you should use std::unordered_map<int,Ticket> instead and use id as a key. Then this function would be:
Ticket Championship::findTicketByFanID(unsigned int id)
{
hTabTickets::iterator it = tickets.find( id );
if( it == tickets.end() ) // not found do something
throw std::runtime_error( "Ticket not found" );
return it->second;
}
hTabTickets::iterator it = tickets.begin()
'it' variable's type neither a Ticket, neither a Ticket*, it's a pair which contains key and value variables, you are using it wrong, probably you need to write:
it->second
to access the Ticket itself
Related
I am trying to know in the caller whether the value in the underlying map exists, and if so, return a reference/iterator to it since the caller needs its access.
Typically you check whether the element exists in a map by verifying whether the returned iterator == map.end(), but here there's no way in the caller to tell that
class A
{
std::unordered_map<int,int> m = { {5,100} };
public:
std::unordered_map<int,int>::iterator get(int key)
{
std::unordered_map<int,int>::iterator it = m.find(key);
return it;
}
};
int main()
{
A a;
auto keyIter = a.get(5);
// if keyIter is not m.end() ...
// if keyIter is in m ...
}
Why return an iterator at all? Surely the right way would be to return the int if it exists? If so you can use optional to indicate if it is valid or not:
std::optional<int> get(int key) {
std::unordered_map<int,int>::iterator it = m.find(key);
if (it != m.end())
return it->second;
return std::nullopt;
}
Usage would then be nice and clean:
auto value = a.get(5);
if (value) {
use_value(*value);
} else {
std::cout << "Key 5 is not in the map\n";
}
I need FindIngredient to return a const Ingredient & when searching by Ingredient.name. I need to used unordered_set. So I think I need 2 of them one to contain the objects and another one just to contain the string attributes name in order to search by string.
Is my approach ok or can I achieve it using only unordered_set<Ingredient> ingredients?
Following with my approach what how do I return in FindIngredient function? I want to return the object in ingredients whose attribute name is the one found searching in ingredientsByName
class Ingredient {
public:
string Name;
int Price;
string Description;
};
class Pizzeria {
unordered_set<string> ingredientsByName;
unordered_set<Ingredient> ingredients;
public:
void AddIngredient(const string &name, const string &description, const int &price)
{
if(ingredientsByName.find(name) == ingredients.end())
{
throw "Ingredient already inserted";
}
else
{
Ingredient ingredient;
ingredient.Name = name;
ingredient.Price = price;
ingredient.Description = description;
ingredients.insert(ingredient);
ingredientsByName.insert(ingredient.Name);
ingredients.insert(ingredient);
}
}
const Ingredient &FindIngredient(const string &name) const
{
if(ingredientsByName.find(name) == ingredients.end())
{
throw "Ingredient not found";
}
else
{
return // how Do I return the corresponding Ingredient ???
}
}
}
If you don't mind slower algorithm of search (O(N) time) then you can do a simple loop:
for (auto it = ingredients.begin(); it != ingredients.end(); ++it)
if (it->Name == name)
return *it; // Return found ingredient.
throw "Ingredient not found";
Loop above can be made even shorter:
for (auto const & e: ingredients)
if (e.Name == name)
return e; // Return found ingredient.
throw "Ingredient not found";
If you want fast algorithm of search (O(Log N) time) then instead of set you may use std::unordered_map, i.e.:
unordered_map<string, Ingredient> ingredientsByName;
adding to map:
ingredientsByName[ingredient.Name] = ingredient;
then search in it:
auto it = ingredientsByName.find(name);
if (it == ingredientsByName.end())
throw "Ingredient not found";
return it->second; // Returns found ingredient.
If you need sorted order of elements (by key) then you can use std::map instead of std::unordered_map, map is always sorted by key (but 2-5 times slower in search and inserting for thousands of elements), while unordered_map is always unsorted (but 2-5 times faster in search and inserting).
If you want to store ingredients separately then you may use std::shared_ptr:
unordered_map<string, shared_ptr<Ingredient>> ingredientsByName;
vector<shared_ptr<Ingredient>> ingredients;
// Adding element:
shared_ptr<Ingredient> ingredient = make_shared<Ingredient>();
ingredient->Name = "abc";
// Also fill other fields.
ingredients.push_back(ingredient);
ingredientsByName[ingredient->Name] = ingredient;
// Search:
auto it = ingredientsByName.find(name);
if (it == ingredientsByName.end())
throw "Ingredient not found";
return *it->second; // Returns found ingredient.
You could use std::find_if with a suitable lambda function to look using the Name member of Ingredient.
And you need to save the result in a variable that you can use when returning.
Perhaps something like this:
const Ingredient &FindIngredient(const string &name) const
{
auto result = std::find_if(begin(ingredients), end(ingredients), [&name](Ingredient const& ingredient)
{
return ingredient.Name == name;
});
if (result == end(ingredients))
{
throw "Ingredient not found";
}
return *result;
}
I have a method that checks if there is an element in a list with the same name as the parameter. If it's found, I want to return a pointer to it, and if the method does not find anything, then return a null pointer. Here's the function:
Place *PlacesMap::Check(string name) {
for (list<Place>::iterator iterator = map.begin(); iterator != map.end(); iterator++) {
Place current = *iterator;
if (current.GetName() == name)
return &*iterator;
}
return nullptr;
}
A Place has a Name and Info, both strings, that is the class:
class Place {
private:
string name;
string info;
public:
Place();
Place(string name, string info);
inline string GetName() {
return name;
}
inline string GetInfo() {
return info;
}
inline void Set(string newName, string newInfo) {
name = newName;
info = newInfo;
}
};
When I insert another Place in the list of Places, if there is already an element with the same name, it only updates the info. To do that, I iterate through the list, and check if there is already a Place with that name, and if there is I update the info, that is working fine. But the when I try to insert a Place that already exist, it updates the info, and after checking it with the Check method, it still shows the old info. What I'm doing wrong on the Check method?
This is the implementation of the insert method:
void PlacesMap::Insert(Place p) {
if (!map.empty()) {
for (list<Place>::iterator iterator = map.begin(); iterator != map.end(); iterator++) {
Place current = *iterator;
if (current.GetName() == p.GetName()) {
current.Set(p.GetName(), p.GetInfo());
} else if ((iterator == --map.end())) {
map.insert(iterator, p);
counter++;
}
}
} else {
map.insert(map.end(), p);
counter++;
}
}
Problem is you try to get the copy of element in line:
Place current = *iterator;
in your Insert method code. You need to use reference instead. Please, try:
Place ¤t = *iterator;
This part of the Insert function
Place current = *iterator;
if (current.GetName() == p.GetName()) {
current.Set(p.GetName(), p.GetInfo());
creates a copy of the node as current, and then updates the copy. It should probably do
iterator->Set(p.GetName(), p.GetInfo());
BTW, using the variable name iterator for an object of type iterator, isn't the best idea. I hesitated quite a bit before writing this line of code.
Say I have an object containing a value. I wish to get the index of object with a particular value from a list of objects. I use the below code to do it,
int MyClass::getIndex(list& somelist, int requiredValue)
{
for( i=0; i != somelist.count(); ++i)
{
if(somelist.at(i)->value() == requiredValue)
return i;
else
continue;
}
return -1;
}
How to avoid the "doesn't return a value on all code paths" warning without using an iterator?
You must return T from the function in any case. If the value possibly does not exists in the list you have options:
return default value (nullptr for pointer for example, -1 for integer possibly)
use boost::optional<T>
return end iterator:
std::list<int>::iterator find_something(std::list<int> &my_list)
{
for (auto it = my_list.begin(); it != my_list.end(); ++it)
{
if (cond)
{
return it;
}
}
return my_list.end();
}
Also
If you just want to find the iterator to some value, use std::find:
auto it = std::find(my_list.begin(), my_list.end(), my_value);
Finally
Don't use list if you need access by index. Use vector in that case
The answer to this question is the same as the answer to "What do you want to happen when the element is not found?".
I'm having a bit of trouble trying to get class association's to work correctly.
I have a vector of class objects, named Items. Each item has a values such as a name, price, etc. Inside the Items class there are setters and getters to change the values and to return them.
std::string choice; // users choice
ListOfOrders::iterator iter = orderList->end(); iter--;
// the last order inserted, ignore this this is used to get the last order so
//we can pass the items to it (the order class has a vector of pointers
//(items) that we are trying to pass to now.)
ListOfItems::iterator itemiter; // make the items iter
listItems(itemList); // function in the main that returns the list of items using the getters and a vector iterator.
while(choice != "x") // until the user quits
{
// here is my prob, ofc, i can just compare the users entered choice of item (name) to the iterator because thats just returning a pointer to the class object, what i need to do it call the getName() getter from each of the objects and comparer that
(*itemiter)->getName() = find (itemList->begin(), itemList->end(), choice);
if (itemiter == itemList->end())
{
std::cout << "sorry item not found please try again." << std::endl;
}
else
{
(*iter)->addItem(*itemiter); // pass the item object off to the order object's vector of items.
}
}
I know something like this(see below(haven't compiled it, just quickly typed it to give you a idea)) could be used and it would work, but there must be a better way right?
std::string choice; // users choice
cin >> choice;
ListOfOrders::iterator iter = orderList->end(); iter--; // the last order inserted
if(lookForItem(choice))
{
std::cout << "Yes\n";
}
else
{
std::cout << "no\n";
}
bool lookForItem(std::string choice)
{
ListOfItems::iterator itemiter; // make the items iter
itemiter = itemList->begin();
while(itemiter != itemList->end())
{
if((*itemiter)->getName() == choice)
{
(*iter)->addItem(*itemiter);
}
iter++;
}
return false;
}
In modern C++, this is fairly simple with a lambda:
auto it = std::find_if(itemList.begin(), itemList.end(),
[&choice](Item const & x) { return x.name == choice; } );
It's not hard to spell out the lambda as a traditional predicate, of course:
struct FindItemByName
{
FindItemByName(std::string const & s) : choice(s) { }
bool operator()(Item const & x) const { return x.name == choice; }
private:
std::string const & choice;
};
ListOfItems::iterator it
= std::find_if(itemList.begin(), itemList.end(), FindItemByName(choice));
Something close to this should work:
#include <vector>
#include <algorithm>
#include <functional>
struct Item {
std::string name;
// two items are equal, when their name is equal
bool operator==(const Item& x) { return name == x.name; }
};
struct Cmp_item_by_name {
bool operator()(const Item& x, const std::string& y) { return x == y; }
};
typedef std::vector<Item> Items;
Items items;
Items::const_iterator
findItem(const std::string& name) {
return std::find_if(items.begin(), items.end(), std::bind2nd(Cmp_item_by_name(), name));
}
This uses the deprecated bind2nd. Don't use this, as long as you don't understand it. We have great book lists on SO.