Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to store objects for later use and make them searchable

At the moment I am using a vector to store pointers to the object every time one is made, but that feels a little silly. There's probably a better way, but I haven't found it.

What I'm doing:                      Example usage:

prototype

The problem:

  1. If I want to retrieve a certain Date I have to go over all items in the vector to see if RecPaymentsStack.stackDate matches the date the user requested.
  2. The RecPaymentStack is actually completely useless at the moment because what I should be doing, is, when adding a new item, checking if a "RecPaymentStack.stackDate" has already been made for the new item's Date property, and if so add the new pointer to "RecPayments" to an array of pointers inside the "RecPaymentStack" object. But how?

I'm probably unnecessarily complicating things (something I do a lot) so an explenation on how something like this should be done would be very nice.

Detailed info: (in case I was being too vague)

The below example is supposed to resemble a calendar that can hold certain items (RecPayments) and those items are grouped by their date (RecPaymentsStack).

struct RecPayments
{
    std::string name;
    Date* date;
    float cost;
};

struct RecPaymentsStack
{
    Date* stackDate; //This stack's date
    RecPayments * thePaymentItem; //Hold pointer to the actual item
};

And here's how I'm currently storing them

std::vector<RecPaymentsStack*> RecPaymentsVector; //This vector will hold pointers to all the Recurring Payments

void addRecurring(std::string theDate,std::string theName,float theCost)
{
    //New recurring payment
    RecPayments * newPaymentItem = new RecPayments;
    //Set recurring payment properties
    newPaymentItem->name = theName;
    newPaymentItem->date = new Date(stringToChar(theDate));
    newPaymentItem->cost = theCost;

    //Add recurring payment to stack
    RecPaymentsStack * addToStack = new RecPaymentsStack;
    addToStack->stackDate = new Date(stringToChar(theDate));
    addToStack->thePaymentItem = newPaymentItem;

    //Add pointer to RecPaymentsStack to vector
    RecPaymentsVector.push_back(addToStack);
}

So to retrieve the items for a given date, I am currently going over all pointers in the vector to see if the "stackDate" property matches the requested date, and if so I use the "thePaymentItem" property to show the actual item.

void getItemsNow(Date requestedDate)
{
    std::cout << "Showing Dates for " << requestedDate << std::endl;
    unsigned int i;
    for(i=0;i<RecPaymentsVector.size();i++) //Go over all items in vector
    {
        Date dateInVector(*RecPaymentsVector[i]->stackDate); //Get the date from the vector
        if(dateInVector == requestedDate) //See if Date matches what the user requested
        {
            //Date matched, show user the item properties.
            std::cout << "Date: " << dateInVector <<
                " has name: " << RecPaymentsVector[i]->thePaymentItem->name <<
                " and price " << RecPaymentsVector[i]->thePaymentItem->cost <<
                std::endl;
        }
    }
}

3 problems with this:

  1. Going over all items in the vector is highly inefficient if I only need a couple of pointers
  2. The RecPaymentStack is actually completely useless at the moment because what I should be doing, is, when adding a new item, checking if a "RecPaymentStack.stackDate" has already been made for the new item's Date property, and if so add the new pointer to "RecPayments" to an array of pointers inside the "RecPaymentStack" object. But how?
  3. All of this feels extremely silly to begin with.. there's probably a much easier/professional way to do this but I can't find out what, probably because I'm still thinking like a PHPer.

So the general idea here is that I end up doing something like (silly example)

for each RecPaymentsStack->stackDate //For each unique Date, show it's children items.
{
    cout << "The Date is " CurrentRecPaymentsStack->stackDate and it holds the following items:
    for each CurrentRecPaymentsStack->thePaymentItem //This would now be an array of pointers
    {
        cout << "item name " CurrentRecPaymentsStack->thePaymentItem->name << " with cost " << CurrentRecPaymentsStack->thePaymentItem->cost << endl;
    }
}

Which would basically go over all the unique "RecPaymentsStack" objects (unique determined by it's "Date" property) and for each Date it would then show it's "children" from the RecPayments struct.

And there has to be some way to search for a particular date without having to go over all the available ones.

like image 902
natli Avatar asked Dec 04 '11 22:12

natli


2 Answers

Rather than using a vector to manage your items, you should replace your RecPaymentsStack instance with a std::multimap. The key type is your Date structure, the value type is RecPayments (which I would change to the singular form RecPayment). Small example (untested):

typedef std::multimap<Date, RecPayment> RecPaymentsByDateMap;
typedef std::pair<RecPaymentsByDateMap::iterator, 
                  RecPaymentsByDateMap::iterator>
                                        RecPaymentsByDateMapIters;

RecPaymentsByDateMap payments_by_date;

RecPaymentsByDateMapIters findByDate(Date date) {
  return payments_by_date.equal_range(date);
}

...

// find all payments with the given date
RecPaymentsByDateMapIters iters = findByDate(...);
for (RecPaymentsByDateMap::iterator it = iters.first;
     it != iters.second;
     ++it)
{
  std::cout << "Payment " << it->second.name << std::endl;
}
like image 83
Niklas B. Avatar answered Nov 17 '22 20:11

Niklas B.


I might design it like this -- this is just a loose idea, details should be adjusted according to your requirements:

#include <deque>
#include <map>
#include <string>

struct RecPayment
{
    std::string name;
    Date        date;
    float       cost;
};

struct RecPaymentsStack
{
    Date stackDate;
    std::deque<RecPayment> thePaymentItem;

    bool operator<(RecPaymentsStack const & rhs) const
    {
      return stackDate < rhs.stackDate;
    }

    explicit RecPaymentsStack(Date const & d) : stackDate(d) { }
};

typedef std::multimap<RecPaymentsStack> PaymentsCollection;

Now you can insert elements:

PaymentsCollection payments;

{
  auto it = payments.emplace(Date("..."));
  it->thePaymentItem.emplace_back(Payment{name1, date1, cost1});
  it->thePaymentItem.emplace_back(Payment{name2, date2, cost2});
  it->thePaymentItem.emplace_back(Payment{name3, date3, cost3});
}
like image 24
Kerrek SB Avatar answered Nov 17 '22 19:11

Kerrek SB