Search code examples
c++stdunordered-set

Searching an unordered set of objects by attribute and returning the corresponding object


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 ???
            }
        
        } 
    }

Solution

  • 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.