I have a container<std::deque<T>> and a const T*ptr which I know points to an object in one of the contained deques. Now, I like to know (1) which deque it comes from and (2) its index in that one. How to get that info?
I'm aware that I can iterate over all objects, but there ought to be a faster solution, ought it not?
Something like (and I havent compiled this but you get the idea):
container<deque<T>> MyCont;
for( auto iter = MyCont.begin(); iter != MyCont.end(); ++iter )
{
auto foundIter = find( *iter.begin(), *iter.end(), MyObject );
if ( foundIter != *iter.end() )
{
dequeIndex = distance( *iter.begin(), foundIter );
containerIndex = distance( MyCont.begin(), iter );
break;
}
}
That's a task for a double iteration:
iterate over container
iterate over current element (a deque) and compare
#
template<typename container> std::pair<container::iterator_t, size_t> FindIndices(const container& c, const container::value_type* x) {
for(auto a = c.begin(); a != c.end(); a++)
for(auto b = a->begin(); b != a->end(); b++)
if(&*b == x)
return std::pair<container::iterator_t, size_t>(a, b-a->begin());
return std::pair<container::iterator_t, size_t>(c.end(), -1);
}
If you can instead store shared_pointers or unique_pointers in your container, you can use a std::map to efficiently retrieve the indices, as long as those don't change.
If only the queue does not change, then there is still some potential for savings.
Related
for (Shape *i : shapes) {
for (Shape *j : shapes) {
if (i != j) {
if (check(i,j)){
shapes.erase(remove(shapes.begin(), shapes.end(), i), shapes.end());
this causes an error because it's going to carry on iterating even though i does not exist, my question is how do I cleanly do this? currently I get an error "vector iterator not incrementable"
Can i just exit the second loop and continue in the first one?
You cannot erase elements from a vector when you are iterating it by for range loop, as internally it uses iterators that would be invalidated. This should work:
auto end = shapes.end();
for( auto it = shapes.begin(); it != end; ++it ) {
end = shapes.erase( std::remove_if( std::next( it ), shapes.end(), [it]( Shape *s ) {
return check( *it, s );
}, shapes.end() );
}
Note this code is slightly more effective than yours but it assumes that check( s1, s2 ) == check( s2, s1 ), if you can change your check() function to do strict ordering comparison, rather than equivalence, then you would be able to use std::sort and std::unique which are even more effective.
You can't modify the positioning of your shapes elements while using ranged-based for loops. The range loop uses iterators internally, and erasing vector elements invalidates existing iterators.
Try something more like this instead:
auto iter = shapes.begin();
auto end = shapes.end();
while (iter != end) {
auto iter2 = shapes.begin();
bool erased = false;
while (iter2 != end) {
if ((iter != iter2) && check(*iter, *iter2)) {
iter = shapes.erase(iter);
end = shapes.end();
erased = true;
break;
}
++iter2;
}
if (!erased)
++iter;
}
Alternatively, maybe something more like this would also work:
shapes.erase(
std::remove_if(shapes.begin(), shapes.end(),
[shapes&](Shape *i) {
for (Shape *j : shapes) {
if ((i != j) && check(i, j)) {
return true;
}
}
return false;
}
),
shapes.end()
);
You cannot use a range-for loop in this case. Instead use a standard loop with iterators:
for (auto iter = shapes.begin(); iter != shapes.end(); iter++)
I'm trying to write a program where given a vector, you use iterators to compare the first and last number of the vector, then moves in and compares the next ones. I wrote the for loop to do that, but am unsure how to make it stop once they reach the center of the vector.
For the for loop I have:
for (a = v.begin(), b = v.rbegin(); a != v.end(), b != v.rend(); a++, b++)
where a is the forward iterator and b is a backwards iterator.
My assumption is that I need to change the condition of the for loop, but I'm unsure to what.
So bear in mind that std::vector<T>::iterator is a random-access iterator, which means that it has operator< defined.
Using this, and using the std::reverse_iterator<Iterator>::base() member function, we can rewrite your for-loop to the following:
auto a = v.begin();
auto b = v.rbegin();
for (; a < b.base(); ++a, ++b)
{
// Do stuff...
}
First of all you need to use && and not the , operator in the comparison, which doesn't do what you think it does.
For your specific question you just keep going until both iterators reach each other, you can obtain the underlying std::iterator of a std::reverse_iterator through base(), eg:
template<typename T> bool isPalindrome(const std::vector<T>& data)
{
for (auto it = data.begin(), it2 = data.rbegin(); it != data.end() && it2 != data.rend() &&
it != it2.base(); ++it, ++it2)
if (*it != *it2)
return false;
return true;
}
Im trying do delete a object pointer in a vector when a user wants to delete an object. Im trying to:
Delete the pointer
Remove the pointer from the vector
I have in a DataBase class a map of strings to vectors. And the vectors are containing pointers to objects. Like this:
Definition of objectMap and objectector in "database.h"
private:
typedef std::vector<Object *> objectVector;
typedef std::map<std::string, objectVector> objectMap;
objectMap objects;
Implementation of deleteId in "database.cpp"
bool DataBase::deleteId(int id)
{
// vectors
for(objectMap::iterator vec = objects.begin(); vec != objects.end(); ++vec)
{
objectVector v = vec->second;
// objects
for(objectVector::iterator obj = v.begin(); obj != v.end(); ++obj)
{
if((*obj)->getId() == id)
{
delete *obj; // pointer
*obj = 0;
v.erase(obj); // erase from vector
modified = true;
return true;
}
}
}
return false;
}
Im currently trying to the debug the program, and I am printing out the whole map of objects to see if the deletion worked. The problem is that the object pointer is still there after the deletion even if the function confirmed the deletion (i.e returned true). Can someone please explain whats going on?
objectVector v = vec->second;
This copies the vector by value and thus you're referring to the copy, not to the real vector.
Suggesting to change that line into
objectVector& v = vec->second; // Reference to the "real" vector
No problems in invalidating your obj iterator since you're quitting the function as soon as you encounter the deletion element.
Your code is wrong at least because you did not erase an element in the original vector. Use reference to the vector
for(objectMap::iterator vec = objects.begin(); vec != objects.end(); ++vec)
{
objectVector &v = vec->second;
Also consider an approach when if after the erasing of an element in a vector the vector becomes empty then maybe you shoulod also erase the corresponding element in the map.
For example )without testing)
bool DataBase::deleteId(int id)
{
bool modified = false;
for ( auto it1 = objects.begin(); !modified && it1 != objects.end(); ++it1 )
{
auto it2 = std::find_if( it1->second.begin(), it1->second.end(),
[&]( Object *o ) { return ( o->getId() == id ); } );
modified = it2 != it1->second.end();
if ( modified )
{
delete *it2;
it1->second.erase( it2 );
if ( it1->second.empty() ) it1 = objects.erase( it1 );
}
}
return modified;
}
How to get random pointer form list of pointers ?
I have simple custom class Buildings and list
std::list<Buidldings*> temp;
size of temp is greater than zero. How to get random pointer from list ( 0 - temp.size)?
Use std::rand to pick an appropriate index and then advance an iterator:
assert( !temp.empty() );
std::list<Buidldings*>::const_iterator it = temp.begin();
std::advance( it, std::rand() % temp.size() );
Buidldings *p = *it;
Try this:
template<typename ContainerType >
typename ContainerType::iterator get_random(ContainerType & container)
{
ContainerType::iterator iter = container.begin();
std::advance(iter,rand() %container.size());
return iter ;
}
template<typename ContainerType >
typename ContainerType::const_iterator get_random(const ContainerType & container)
{
... (same as above)
}
From here
I am storing values in a std::map
I am finding two values in the map, and I want to iterate between the first through to the last item - however the <= operator is not implemented, so I can't do somethimng like this:
void foobar(const DatedRecordset& recs, const double startstamp, const double endtstamp)
{
DatedRecordsetConstIter start_iter = recs.lower_bound(startstamp), end_iter = recs.lower_bound(endtstamp);
// Can't do this .... (<= not defined)
//for (DatedRecordsetConstIter cit = start_iter; cit <= end_iter; cit++ )
/ So have to resort to a hack like this:
for (DatedRecordsetConstIter cit = start_iter; cit != recs.end(); cit++ ) {
if ((*cit).first <= (*end_iter).first){
//do something;
}
else
break;
}
}
}
Is there a more elegant way of iterating between two known iterators?
Use != instead of <= and it will do what you want it to do.
void foobar(const DatedRecordset& recs, const double startstamp, const double endtstamp)
{
DatedRecordsetConstIter start_iter = recs.lower_bound(startstamp),
end_iter = recs.upper_bound(endtstamp);
for (DatedRecordsetConstIter cit = start_iter; cit != end_iter; ++cit) {
}
}
There isn't a <= operator for std::map<>::iterator, but using != on end_iter should do basically the same thing. If you want to include the end iterator itself in the iteration, use something like a do loop to do the != test at the end.
struct ManipulateMatchingPairs {
template<class K, class V>
void operator()(const std::pair<K,V>& p) const {
// do with p.second as you please here.
}
};
// ...
std::for_each(start_iter, end_iter, ManipulateMatchingPairs());
You have to use the != operator. I believe this is because a std::map isn't necessarily contiguous in memory (so the <= operator wouldn't make much sense, whereas a std::vector would), I could be wrong though
The STL for_each algorithm also will not include the ending iterator in the loop. You could always increcment end_iter and just use for_each so that it will be included, though.
void foobar(const DatedRecordset& recs,
const double startstamp,
const double endtstamp)
{
DatedRecordsetConstIter start_iter = recs.lower_bound(startstamp);
DatedRecordsetConstIter end_iter = recs.lower_bound(endtstamp);
if(end_iter != recs.end())
++end_iter;
for_each(start_iter, end_iter, []()
{
//do something inside the lambda.
});
}
Something like that maybe? I didn't give it a compile check ...
If you want to include the end iterator in the loop, you can increment your end-condition iterator ++end_iter. After that the loop with cit != end_iter does the same as you intend to do with cit <= end_iter before incrementing.