简体   繁体   中英

finding objects in std::map from std::vector

A quick question for you C++ experts:

In the project that I'm working on, there is a class containing pointer to objects in a vector:

    std::vector<object*> objects;

and there is a struct that adds an offset to the object (Point2D is obviously x,y coordinates)

    struct DraggedObject{
    object* obj;
    Point2D offset;

    DraggedObject():obj(NULL),offset(Point2D(0,0)){}
};

And whenever you drag an object it adds it to a std::map that looks like this:

    std::map <int, DraggedObject> dragged_objects;

So my problem is this: there is a loop in which i need to move this objects, but skip the dragged ones. My question is: Is there a way to loop through the objects vector and find out if the object is on the dragged_object map?

I'm doing something like this:

for(std::vector<object*>::iterator it = objects.begin(); it != objects.end(); it++){
    if(//Object is not in dragged_objects){
        (*it)->move_to( Point2D( //Some point);
    }else{
        (*it)->move_to (Point2D (//Point of dragged object);
}

But I don't know how to do this comparisson...

Sorry for the newbie question.

A std::map is optimized for fast lookup by key (in this case the int value) not fast lookup by value (your object*). So your only bet is to iterate the whole map until you find it. When the map is small and the code section isn't performance-critical, you can do that. When performance matters, you could add an additional std::set and store all objects in it which are currently being dragged, or you could make the objects themselves be aware if they are currently being dragged, and add a isDragged() method to your object class.

You could alter your approach and iterate your map of dragged objects, do the work you need done on each dragged object and then add the object to a temporary std::vector<object*> . When your loop is finished you can go through your vector and only do something to the elements that aren't in your temporary vector.

I believe you might be able to make use of set_difference . You will have to implement custom comparator and also you will have to order the elements in the vector. Unless you do that I don't think you can do any better than performing a find for each element in the vector to verify if it is present in the map.

If possible collect Dragged objects in a container( map ) with object* key, eg std::map <object*, DraggedObject> instead of std::map <int, DraggedObject> . Then you could write your if like this:

 if(dragged_objects.count(*it)){
        (*it)->move_to( Point2D( //Some point);
    }else{
        (*it)->move_to (Point2D (//Point of dragged object);

One approach you could use is replace the vector with a list and have this list only contain undragged objects.

Then you would iterate through this list. If you want all the objects, you iterate through both the ilst and the map. Not sure how important the order is for doing that, but if the id of the object is significant (that you store in the map) you can iterate "merged" through the list and map, based on the id inside the map.

The reason list is better than vector in this case is if you are dragging objects out of the middle of your collection, where removing from a list is constant time.

Since you said that object ID is they key in dragged_objects map, you can use find method of map. I believe it should go something like this.

std::map::iterator miter;
for (auto &obj : objects)
{
    miter = dragged_objects.find(obj.id);
    if (miter != dragged_objects.end()) {
        /* do for non-dragged objects */
    } else {
        /* do for dragged objects */
    }
}

Adjust for non-C++11 compilers as necessary.

Where does the int come from which is used to index into the map? If it's an identifier extracted from object , then all you have to do is extract it in your loop, and see if it is present in the map (using map::find ). Otherwise, you'll either need an additional map, indexed with object*, or you'll need to iterate through all of the dragged_objects , looking at each one to see if it has the address you want ( std::find_if ). Depending on the number of dragged_objects , this could be slow; but normally, I'd guess that dragged_objects is either empty or only has a single entry. If this is the case, the linear search each time through your loop is perfectly acceptable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM