1

So I am placing objects in a vector. I want to drop them in order as they are added. the basics of the object are

class myObj  {
  private:
     string firstName;
     string lastName;
  public:
     string getFirst;
     string getLast;    
}

I also have a vector of these objects

vector< myObj > myVect;
vector< myObj >::iterator myVectit = myVect.begin();  

when I add a new object to the vector I want to find where it should be placed before inserting it. Can I search a vector by an object value and how? This is my first attempt

void addanObj (myObj & objtoAdd){
  int lowwerB = lower_bound(
                myVect.begin().getLast(), myVect.end().getLast(), objtoAdd.getLast()
                );
  int upperB = upper_bound(
               myVect.begin().getLast(), myVect.end().getLast(), objtoAdd.getLast()
               );

from there i plan to use lowwerB and upper B to determine where to insert the entry. what do I need to do to get this to work or what is a better method of tackling this challenge?

----Follow up----

the error I get when I attempt to compile

 error C2440: 'initializing' : cannot convert from 'std::string' to 'int'
 No user-defined-conversion operator available that can perform this conversion, 
 or the operator cannot be called

The compiler highlights both lower_bound and upper_bound. I would guess it is referring to where I am putting

objtoAdd.getLast()

-----More Follow up----------------- THis is close to compiling but not quite. What should I expect to get from lower_bound and upper_bound? It doesnt match the iterator i defined and im not sure what I should expect.

void addMyObj(myObj myObjtoadd)
    vector< myObj>::iterator tempLB;
    vector< myObj>::iterator tempUB;
    myVectit= theDex.begin();
    tempLB  = lower_bound(
              myVect.begin()->getLast(), myVect.end()->getLast(), myObjtoadd.getLast()
              );
    tempUB = upper_bound(
             myVect.begin()->getLast(), myVect.end()->getLast(), myObjtoadd.getLast()
             );
Andreas DM
  • 10,685
  • 6
  • 35
  • 62
Rob McNeil
  • 51
  • 8
  • 1
    Is there any reason you're using a `vector`? It seems like you might want a `std::map` or `std::multimap` which will return an iterator pointing to the inserted object when you call `insert`. It will also keep everything sorted for you. – druckermanly Nov 05 '14 at 03:12
  • 2
    Since there's no logical key vs. value, I think you meant set, not map. – chris Nov 05 '14 at 03:16
  • Also, see here: http://stackoverflow.com/questions/15843525/how-do-you-insert-the-value-in-a-sorted-vector – clstrfsck Nov 05 '14 at 03:21
  • 1
    So - what happened with your "first attempt"? Obviously it won't compile, but did you consider the compiler errors and try to fix them? If you didn't know how to fix them, what specifically about the error message confused you? Why are you asking us after making bugger all effort? – Tony Delroy Nov 05 '14 at 03:45
  • @Tony D Im not asking for people to code this all up for me. Just a direction on how to get it working or a better idea. Even if it was a concept to explore that I have not considered. Im still new at C++ so im sure there are options I am not aware of. – Rob McNeil Nov 05 '14 at 03:54
  • @RobMcNeil well the basic idea of using `std::lower_bound()` is sound, so why not finish the job and see if there's an actual problem? If you want an alternative, one is to switch to a container that keeps itself sorted - namely `std::set`. – Tony Delroy Nov 05 '14 at 04:07
  • @Tony D Thanks, i guess part of my question was "Am I headed in a good direction?" – Rob McNeil Nov 05 '14 at 04:15
  • @RobMcNeil: fair enough ;-), and yes - if you want to use a `vector` then your choice is very sensible. Main thing is that insertion mid-vector forces a copying of all the later vector content one element along to make space for the insertion... that can get very slow if the vector is huge and you're doing a lot of insertions far from the end. `set` uses a balanced binary tree and insertions don't slow down very much - O(log2N) if you know big-O notation - e.g., operations on 1,000,000 elements tend to be twice as slow as for 1,000 - not too bad. – Tony Delroy Nov 05 '14 at 04:19
  • @Tony D Its a class project, speed isnt too important. – Rob McNeil Nov 05 '14 at 04:22
  • @chris yes I did! Too bad I was on mobile and didn't think to check back / edit. Oops! – druckermanly Nov 05 '14 at 04:40
  • @RobMcNeil: The first two parameters of `std::lower_bound` and `std::upper_bound` and their return types are all __iterators__. If those are supposed to be the standard library functions then you're using them incorrectly. – Blastfurnace Nov 05 '14 at 05:14
  • @Blastfurnace I corrected the iterator issue and made other edits< its likely a better situation now. but noe quite done – Rob McNeil Nov 05 '14 at 05:35
  • 1
    @RobMcNeil: You still need to pass __iterators__ to those functions. Change those calls to something like `lower_bound(myVect.begin(), myVect.end(), myObjtoadd.getLast());` – Blastfurnace Nov 05 '14 at 05:43
  • @BlastFurnace that will still a result on what it finds in .getlast () for all of the objects in the vector??? – Rob McNeil Nov 05 '14 at 05:47
  • @RobMcNeil: Most standard library algorithms require two iterators to define the range of the container to operate on. You want to tell those functions to search the entire container, `begin()` to `end()`. The third parameter, in this case, is just the value to compare to. [See the documentation at this link.](http://en.cppreference.com/w/cpp/algorithm/lower_bound) – Blastfurnace Nov 05 '14 at 05:51
  • @RobMcNeil: By the way, this all assumes you've defined an `operator<` for your `myObj` class that takes a `std::string` as the right-hand parameter and compares it to the `lastName` field. – Blastfurnace Nov 05 '14 at 06:00
  • @Blastfurnace I get it now. Im missing some things in my implementation for this to work but it will. Ill get back after I build the Boolean sort control part. Other than that it seems good to go. – Rob McNeil Nov 05 '14 at 06:06

2 Answers2

0

Your calls to std::lower_bound and std::upper_bound are incorrect. The first two parameters must be iterators that define a range of elements to search and the returned values are also iterators.

Since these algorithms compare the container elements to the third parameter value you'll also need to provide correct operator< functions that compare an object's lastName and a std::string. I've added two different compare functions since std::lower_bound and std::upper_bound pass the parameters in opposite order.

I think I have the machinery correct in this code, it should be close enough for you to get the idea.

class myObj {
    private:
        std::string firstName;
        std::string lastName;
    public:
        std::string getFirst() const { return firstName; }
        std::string getLast() const { return lastName; }
};

bool operator<(const myObj &obj, const std::string &value) // used by lower_bound()
{
    return obj.getLast() < value;
}

bool operator<(const std::string &value, const myObj &obj) // used by upper_bound()
{
    return value < obj.getLast();
}

int main()
{
    std::vector<myObj> myVect;
    std::vector<myObj>::iterator tempLB, tempUB;
    myObj objtoAdd;

    tempLB = std::lower_bound(myVect.begin(), myVect.end(), objtoAdd.getLast());
    tempUB = std::upper_bound(myVect.begin(), myVect.end(), objtoAdd.getLast());
}
Blastfurnace
  • 18,411
  • 56
  • 55
  • 70
0

So this is definitely not the best way to go. Here's why:

Vector Size

A default vector starts out with 0 elements, but capacity to hold some number; say 100. After you add the 101st element, it has to completely recreate the vector, copy over all the data, and then delete the old memory. This copying can become expensive, if done enough.

Inserting into a vector

This is going to be even more of a problem. Because a vector is just a contiguous block of memory with objects stored in insert order, say you have the below:

[xxxxxxxzzzzzzzz            ]

if you want to add 'y', it belongs between x and z, right? this means you need to move all the z's over 1 place. But because you are reusing the same block of memory, you need to do it one at a time.

[xxxxxxxzzzzzzz z           ]
[xxxxxxxzzzzzz zz           ]
[xxxxxxxzzzzz zzz           ]
...
[xxxxxxx zzzzzzzz           ]
[xxxxxxxyzzzzzzzz           ]

(the spaces are for clarity - previous value isn't explicitly cleared)

As you can see, this is a lot of steps to make room for your 'y', and will be very very slow for large data sets.

A better solution

As others have mentioned, std::set sounds like it's more appropriate for your needs. std::set will automatically order all inserted elements (using a tree data structure for much faster insertion), and allows you to find particular data members by last name also in log(n) time. It does this by using bool myObj::operator(const & _myObj) const to know how to sort the different objects. If you simply define this operator to compare this->lastName < _myObj.lastName, you can simply insert into the set much quicker.

Alternately, if you really really want to use vector: instead of sorting it as you go, just add all the items to the vector, and then perform std::sort to sort them after all the inserts are done. This will also complete in n log(n) time, but should be considerably faster than the current approach because of the vector insertion problem.

Rollie
  • 4,391
  • 3
  • 33
  • 55
  • Good things to consider for efficiency. but this is a school project that just needs to work. I will keep this in mind next time around. Thanks you for the input!! – Rob McNeil Nov 05 '14 at 15:10