C++: loop iterator after find

Started by
7 comments, last by Brother Bob 12 years, 2 months ago
I'd like to ask how do you get the size of an iterator (i.e. the number of find hits) and how would you actually loop through the results.


std::vector<myObjects::myObjectLib>::iterator findHair;
findHair = std::find_if(myObject.begin(), myObject.end(), HasFaceId(idx));


In this case, findHair is my iterator: I thought I might just use a


int size=findHair.size();


to get its size and then loop the values with findHair.at[0 to size-1] to find the values. But that gives compile errors.
Thanks for any help
Advertisement
[font=courier new,courier,monospace]std::find_if[/font] doesn't return a bunch of iterators. It returns the iterator to the *first* element in the range that satisfies the condition. If you want to iterate over all of the elements that meet a condition, you have to keep refinding. i.e.


std::vector<myObjects::myObjectLib>::iterator findHair;
// This is kinda long, but it's actually not complex
for (findHair = std::find_if(myObject.begin(), myObject.end(), HasFaceId(idx)); findHair != myObject.end(); findHair = std::find_if(findHair + 1, myObject.end(), HasFaceId(idx))
{
// now do something with findHar
}


[edit]

Actually, I'll have to double check if [font=courier new,courier,monospace]findHair = std::find_if(findHair + 1, myObject.end(), HasFaceId(idx))[/font] is prone to any errors, since it's possible [font=courier new,courier,monospace]findHair + 1 == myObject.end()[/font], and [font=courier new,courier,monospace]std::find_if [/font]expects the valid range [first, last) to be given. I'm assuming that it checks if [font=courier new,courier,monospace]first == last[/font] before it begins iterating, but I'll have to double check that.

[edit edit]

Node that this code is the same as just doing

std::vector<myObjects::myObjectLib>::iterator findHair;
// This is kinda long, but it's actually not complex
for (findHair = myObject.begin(); findHair != myObject.end(); ++findHair)
{
if (HasFaceId(findHair, idx))
{
// now do something with findHar
}
}
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]
Thanks for the example Cornstalk. But how would be possible, for example, to access the set of data returned from a find iterator. I mean, say you want to copy the items returned from the search in a new vector.

Thanks for the example Cornstalk. But how would be possible, for example, to access the set of data returned from a find iterator. I mean, say you want to copy the items returned from the search in a new vector.

That part was left out of C++ by accident (no really, it was). You can use something like [font=courier new,courier,monospace]std::remove_copy_if[/font], but you'll have to remember you'll have to invert your logic (that is, you'll be copying the things for which the result is false rather then the ones for which the result is true, so you'll have to design your predicate function accordingly).
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]
You can treat the iterator as a pointer; dereference it to get a reference to the object and use the -> operator to access the members of the object.

But if you want to copy it to another list, you can use the pre-defined algorithms in the standard library.
[source]
std::remove_copy_if(myObject.begin(), myObject.end(), std::back_inserter(results), std::not1(HasFaceId(idx)));
[/source]
results is a container (it doesn't have to be a vector, can be pretty much any sequence container) to store the results. I hope it is correct, I have only briefly verified it.
Thanks, remove_copy_if works just fine.
Out of curiousity, I suppose there is nothing binary-like to speed it up... rolleyes.gif

Thanks, remove_copy_if works just fine.
Out of curiousity, I suppose there is nothing binary-like to speed it up... rolleyes.gif

I suppose you're hinting at binary search and some algorithm that is better than O(n). You can't do better than O(n) in this case, because you have to check every element to see if it satisfies the condition so you know if that element should be copied or not.
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]
I still have troubles using the predicate to test if values match.
In this example I have three point structs that I want to compare with a vector:


Point3D F0,F1,F2;

//check point F0
std::vector<myObjects::myObjectLib>::iterator follicleToHair0;
follicleToHair0 = std::find_if(myObject.begin(), myObject.end(), isMatching(F0));
if (follicleToHair0!=myObject.end())
{
int t=(*follicleToHair0).ID;
QMessageBox::warning( 0, QObject::tr( "Error" ), QObject::tr( QString::number(t) ), QObject::tr( "&OK" ) );
}

//check point F1
std::vector<myObjects::myObjectLib>::iterator follicleToHair1;
follicleToHair1 = std::find_if(myObject.begin(), myObject.end(), isMatching(F1));
if (follicleToHair1!=myObject.end())
{
int t=(*follicleToHair1).ID;
QMessageBox::warning( 0, QObject::tr( "Error" ), QObject::tr( QString::number(t) ), QObject::tr( "&OK" ) );
}

//check point F2
std::vector<myObjects::myObjectLib>::iterator follicleToHair2;
follicleToHair2 = std::find_if(myObject.begin(), myObject.end(), isMatching(F2));
if (follicleToHair2!=myObject.end())
{
int t=(*follicleToHair2).ID;
QMessageBox::warning( 0, QObject::tr( "Error" ), QObject::tr( QString::number(t) ), QObject::tr( "&OK" ) );
}


And here is the predicate:


struct isMatching {
isMatching(Point3D_t myPoint) : id(myPoint) { }
bool operator()(const myObjects::myObjectLib &obj)
{
if (obj.hairControlPoint[0].x < id.x) return true;
if (obj.hairControlPoint[0].x > id.x) return false;
if (obj.hairControlPoint[0].y < id.y) return true;
if (obj.hairControlPoint[0].y > id.y) return false;
return obj.hairControlPoint[0].z < id.z;
}
Point3D id;
};


Since these are float, I was taught in another thread that compare for floats should be done this way.

The problem is that the result returned by the three checks are all the same. There should be something very wrong about what I did here.
The predicate for find_if shall return true if you have a match, and false if you don't have a match. That is, you must implement equality. Your predicate looks more like it's implementing a less-than comparison for ordering (for example, as used by std::sort). For example, the predicate for obj=(1, 0, 0) and id=(100, 0, 0) returns true since 1 < 100, and find_if will return that element as a match, but they are not equal.

You want this instead.
[source]
bool operator()(const myObjects::myObjectLib &obj)
{
return obj.hairControlPoint[0].x == id.x && obj.hairControlPoint[0].y == id.y && obj.hairControlPoint[0].z == id.z;
}
[/source]

Since you said they are floatingpoint values, you may want to compare against some limit to determine if they are close rather than identical though. abs(a-b)<eps is a common way to determine if a and b are equal to within eps, so do that test for all three value pairs.

This topic is closed to new replies.

Advertisement