Checking if reducing iterator points to a valid element - c++

I need to know if I can reduce the iterator and have a valid object. The below errors out because I reduce the iterator by 1 which doesn't exist. How can I know that so I don't get the error?
ticks.push_front(Tick(Vec3(0, 0, 5), 0));
ticks.push_front(Tick(Vec3(0, 0, 8), 100));
ticks.push_front(Tick(Vec3(0, 0, 10), 200));
bool found = false;
list<Tick, allocator<Tick>>::iterator iter;
for (iter = ticks.begin(); iter != ticks.end(); ++iter)
{
Tick t = (*iter);
if (214>= t.timestamp)
{
prior = t;
if (--iter != ticks.end())
{
next = (*--iter);
found = true;
break;
}
}
}
I'm trying to find the entries directly "above" and directly "below" the value 214 in the list. If only 1 exists then I don't care. I need above and below to exist.

After your edits to the question, I think I can write a better answer than what I had before.
First, write a comparison function for Ticks that uses their timestamps:
bool CompareTicks(const Tick& l, const Tick& r)
{
return l.timestamp < r.timestamp;
}
Now use the function with std::upper_bound:
// Get an iterator pointing to the first element in ticks that is > 214
// I'm assuming the second parameter to Tick's ctor is the timestamp
auto itAbove = std::upper_bound(ticks.begin(), ticks.end(), Tick(Vec3(0, 0, 0), 214), CompareTicks);
if(itAbove == ticks.end())
; // there is nothing in ticks > 214. I don't know what you want to do in this case.
This will give you the first element in ticks that is > 214. Next, you can use lower_bound to find the first element that is >= 214:
// get an iterator pointing to the first element in ticks that is >= 214
// I'm assuming the second parameter to Tick's ctor is the timestamp
auto itBelow = std::lower_bound(ticks.begin(), ticks.end(), Tick(Vec3(0, 0, 0), 214), CompareTicks);
You have to do one extra step with itBelow now to get the first element before 214, taking care not to go past the beginning of the list:
if(itBelow == ticks.begin())
; // there is nothing in ticks < 214. I don't know what you want to do in this case.
else
--itBelow;
Now, assuming you didn't hit any of the error cases, itAbove is pointing to the first element > 214, and itBelow is pointing to the last element < 214.
This assumes your Ticks are in order by timestamp, which seems to be the case. Note also that this technique will work even if there are multiple 214s in the list. Finally, you said the list is short so it's not really worth worrying about time complexity, but this technique could get you logarithmic performance if you also replaced the list with a vector, as opposed to linear for iterative approaches.

The answer to your core question is simple. Don't increment if you are at the end. Don't decrement if you are at the start.
Before incrementing, check.
if ( iter == ticks.end() )
Before decrementig, check.
if ( iter == ticks.begin() )
Your particular example
Looking at what you are trying to accomplish, I suspect you meant to use:
if (iter != ticks.begin())
instead of
if (--iter != ticks.end())
Update
It seems you are relying on the contents of your list being sorted by timestamp.
After your comment, I think what you need is:
if (214>= t.timestamp)
{
prior = t;
if (++iter != ticks.end())
{
next = *iter;
if ( 214 <= next.timestep )
{
found = true;
break;
}
}
}
Update 2
I agree with the comment made by #crashmstr. Your logic can be:
if (214 <= t.timestamp)
{
next = t;
if ( iter != ticks.begin())
{
prior = *--(iter);
found = true;
break;
}
}

I think you can do what you want with std::adjacent_find from the standard library <algorithm>. By default std::adjacent_find looks for two consecutive identical elements but you can provide your own function to define the relationship you are interested in.
Here's a simplified example:
#include <algorithm>
#include <iostream>
#include <list>
struct matcher
{
matcher(int value) : target(value) {}
bool operator()(int lo, int hi) const {
return (lo < target) && (target < hi);
}
int target;
};
int main()
{
std::list<int> ticks = { 0, 100, 200, 300 };
auto it = std::adjacent_find(ticks.begin(), ticks.end(), matcher(214));
if (it != ticks.end()) {
std::cout << *it << ' ' << *std::next(it) << '\n';
} else {
std::cout << "not found\n";
}
}
This outputs 200 300, the two "surrounding" values it found.

Related

I don't know why this two-liner works

int countMatches(vector<vector<string>>& items, string ruleKey, string ruleValue) {
int idx = ruleKey == "type" ? 0 : ruleKey == "color" ? 1 : 2, res = 0;
return count_if(begin(items), end(items), [&](const auto &i) { return i[idx] == ruleValue; });
}
This is a problem off of leetcode. I attached the link below.
I will try and say what I think is going on, if anything is wrong - please correct me.
First, we are creating an int variable called idx. We assign it the current value of ruleKey (which is currently a string). If the string is "type", we assign idx the value of 0. If the string is "color", we assign idx the value of 1. If NEITHER of those two conditions pass, we assign idx the value of 2.
I don't know why res = 0 is even in the code, and I have no clue what is going on with the line of code that lies beneath. Especially with the [&](const auto &i) portion of the code.
I will try and say what I think is going on, if anything is wrong - please correct me.
Your understanding of the idx variable is correct.
I don't know why res = 0 is even in the code
It is simply declaring an unused variable res initialized to 0. Multiple variables of the same type can be declared in the same expression, eg:
int a = 0, b = 1;
In this case, yes res doesn't really belong and can safely be removed.
I have no clue what is going on with the line of code that lies beneath. Especially with the [&](const auto &i) portion of the code.
See the documentation for the std::count_if() algorithm and Lambda Expressions.
In a nutshell, std::count_if() loops through a range, calling a predicate for each element, and increments the result each time the predicate returns true. The code is using a lambda, ie an anonymous function type, for that predicate.
So, the code is iterating the items vector, counting how many of its inner vector elements have a value matching ruleValue at the index specified by idx. The code is basically equivalent to this:
int countMatches(vector<vector<string>>& items, string ruleKey, string ruleValue) {
int idx;
if (ruleKey == "type") idx = 0;
else if (ruleKey == "color") idx = 1;
else idx = 2;
int count = 0;
for(auto iter = items.begin(), end = items.end(); iter != end; ++iter) {
const auto &i = *iter;
if (i[idx] == ruleValue) ++count;
}
return count;
}

Inserting multiple values into a vector at specific positions

Say I have a vector of integers like this std::vector<int> _data;
I know that if I want to remove multiple items from _data, then I can simply call
_data.erase( std::remove_if( _data.begin(), _data.end(), [condition] ), _data.end() );
Which is much faster than eraseing multiple elements, as less movement of data is required within the vector. I'm wondering if there's something similar for insertions.
For example, if I have the following pairs
auto pair1 = { _data.begin() + 5, 5 };
auto pair2 = { _data.begin() + 12, 12 };
Can I insert both of these in one iteration using some existing std function? I know I can do something like:
_data.insert( pair2.first, pair2.second );
_data.insert( pair1.first, pair1.second );
But this is (very) slow for large vectors (talking 100,000+ elements).
EDIT: Basically, I have a custom set (and map) which use a vector as the underlying containers. I know I can just use std::set or std::map, but the number of traversals I do far outweighs the insertion/removals. Switching from a set and map to this custom set/map already cut 20% of run-time off. Currently though, insertions take approximately 10% of the remaining run time, so reducing that is important.
The order is also required, unfortunately. As much as possible, I use the unordered_ versions, but in some places the order does matter.
One way is to create another vector with capacity equal to the original size plus the number of the elements being inserted and then do an insert loop with no reallocations, O(N) complexity:
template<class T>
std::vector<T> insert_elements(std::vector<T> const& v, std::initializer_list<std::pair<std::size_t, T>> new_elements) {
std::vector<T> u;
u.reserve(v.size() + new_elements.size());
auto src = v.begin();
size_t copied = 0;
for(auto const& element : new_elements) {
auto to_copy = element.first - copied;
auto src_end = src + to_copy;
u.insert(u.end(), src, src_end);
src = src_end;
copied += to_copy;
u.push_back(element.second);
}
u.insert(u.end(), src, v.end());
return u;
}
int main() {
std::vector<int> v{1, 3, 5};
for(auto e : insert_elements(v, {{1,2}, {2,4}}))
std::cout << e << ' ';
std::cout << '\n';
}
Output:
1 2 3 4 5
Ok, we need some assumptions. Let old_end be a reverse iterator to the last element of your vector. Assume that your _data has been resized to exactly fit both its current content and what you want to insert. Assume that inp is a container of std::pair containing your data to be inserted that is ordered reversely (so first the element that is to be inserted at the hindmost position and so on). Then we can do:
std::merge(old_end, _data.rend(), inp.begin(), inp.end(), data.rend(), [int i = inp.size()-1](const &T t, const &std::pair<Iter, T> p) mutable {
if( std::distance(_data.begin(), p.first) == i ) {
--i;
return false;
}
return true;
}
But I think that is not more clear than using a good old for. The problem with the stl-algorithms is that the predicates work on values and not on iterators thats a bit annoying for this problem.
Here's my take:
template<class Key, class Value>
class LinearSet
{
public:
using Node = std::pair<Key, Value>;
template<class F>
void insert_at_multiple(F&& f)
{
std::queue<Node> queue;
std::size_t index = 0;
for (auto it = _kvps.begin(); it != _kvps.end(); ++it)
{
// The container size is left untouched here, no iterator invalidation.
if (std::optional<Node> toInsert = f(index))
{
queue.push(*it);
*it = std::move(*toInsert);
}
else
{
++index;
// Replace current node with queued one.
if (!queue.empty())
{
queue.push(std::move(*it));
*it = std::move(queue.front());
queue.pop();
}
}
}
// We now have as many displaced items in the queue as were inserted,
// add them to the end.
while (!queue.empty())
{
_kvps.emplace_back(std::move(queue.front()));
queue.pop();
}
}
private:
std::vector<Node> _kvps;
};
https://godbolt.org/z/EStKgQ
This is a linear time algorithm that doesn't need to know the number of inserted elements a priori. For each index, it asks for an element to insert there. If it gets one, it pushes the corresponding existing vector element to a queue and replaces it with the new one. Otherwise, it extracts the current item to the back of the queue and puts the item at the front of the queue into the current position (noop if no elements were inserted yet). Note that the vector size is left untouched during all this. Only at the end do we push back all items still in the queue.
Note that the indices we use for determining inserted item locations here are all pre-insertion. I find this a point of potential confusion (and it is a limitation - you can't add an element at the very end with this algorithm. Could be remedied by calling f during the second loop too, working on that...).
Here's a version that allows inserting arbitrarily many elements at the end (and everywhere else). It passes post-insertion indices to the functor!
template<class F>
void insert_at_multiple(F&& f)
{
std::queue<Node> queue;
std::size_t index = 0;
for (auto it = _kvps.begin(); it != _kvps.end(); ++it)
{
if (std::optional<Node> toInsert = f(index))
queue.push(std::move(*toInsert));
if (!queue.empty())
{
queue.push(std::move(*it));
*it = std::move(queue.front());
queue.pop();
}
++index;
}
// We now have as many displaced items in the queue as were inserted,
// add them to the end.
while (!queue.empty())
{
if (std::optional<Node> toInsert = f(index))
{
queue.push(std::move(*toInsert));
}
_kvps.emplace_back(std::move(queue.front()));
queue.pop();
++index;
}
}
https://godbolt.org/z/DMuCtJ
Again, this leaves potential for confusion over what it means to insert at indices 0 and 1 (do you end up with an original element in between the two? In the first snippet you would, in the second you wouldn't). Can you insert at the same index multiple times? With pre-insertion indices that makes sense, with post-insertion indices it doesn't. You could also write this in terms of passing the current *it (i.e. key value pair) to the functor, but that alone seems not too useful...
This is an attempt I made, which inserts in reverse order. I did get rid of the iterators/indices for this.
template<class T>
void insert( std::vector<T> &vector, const std::vector<T> &values ) {
size_t last_index = vector.size() - 1;
vector.resize( vector.size() + values.size() ); // relies on T being default constructable
size_t move_position = vector.size() - 1;
size_t last_value_index = values.size() - 1;
size_t values_size = values.size();
bool isLastIndex = false;
while ( !isLastIndex && values_size ) {
if ( values[last_value_index] > vector[last_index] ) {
vector[move_position] = std::move( values[last_value_index--] );
--values_size;
} else {
isLastIndex = last_index == 0;
vector[move_position] = std::move( vector[last_index--] );
}
--move_position;
}
if ( isLastIndex && values_size ) {
while ( values_size ) {
vector[move_position--] = std::move( values[last_value_index--] );
--values_size;
}
}
}
Tried with ICC, Clang, and GCC on Godbolt, and vector's insert was faster (for 5 numbers inserted). On my machine, MSVC, same result but less severe. I also compared with Maxim's version from his answer. I realize using Godbolt isn't a good method for comparison, but I don't have access to the 3 other compilers on my current machine.
https://godbolt.org/z/vjV2wA
Results from my machine:
My insert: 659us
Maxim insert: 712us
Vector insert: 315us
Godbolt's ICC
My insert: 470us
Maxim insert: 139us
Vector insert: 127us
Godbolt's GCC
My insert: 815us
Maxim insert: 97us
Vector insert: 97us
Godbolt's Clang:
My insert: 477us
Maxim insert: 188us
Vector insert: 96us

Get the closest element to a given element in an std::set

I have a (sorted) set of unsigned int's. I need to find the closest element to a given number.
I am looking for a solution using the standard library,
my first solution was to use binary search, but STL's implementation only returns if the element exists.
This post, Find Closest Element in a Set, was helpful and I implemented a solution based on std::lower_bound method,
(*Assuming the set has more than 2 elements, no empty/boundary checks are made):
#include <iostream>
#include<set>
#include<algorithm>
#include<cmath>
int main()
{
std::set<unsigned int> mySet = {34, 256, 268, 500, 502, 444};
unsigned int searchedElement = 260;
unsigned int closestElement;
auto lower_bound = mySet.lower_bound(searchedElement);
if (lower_bound == mySet.end()){
closestElement = *(--lower_bound);
}
std::set<unsigned int>::iterator prevElement = --lower_bound;
bool isPrevClosest = std::abs(*prevElement - searchedElement) > std::abs(*lower_bound - searchedElement);
closestElement = isPrevClosest ? *prevElement : *lower_bound;
std::cout << closestElement << std::endl;
return 0;
}
Is there a simpler more standard solution?
I don't think there is a better solution than using .lower_bound. You can wrap your algorithm into a function template:
template<typename Set>
auto closest_element(Set& set, const typename Set::value_type& value)
-> decltype(set.begin())
{
const auto it = set.lower_bound(value);
if (it == set.begin())
return it;
const auto prev_it = std::prev(it);
return (it == set.end() || value - *prev_it <= *it - value) ? prev_it : it;
}
This function handles all corner cases (empty set, one element, first element, last element) correctly.
Example:
std::set<unsigned int> my_set{34, 256, 268, 500, 502, 444};
std::cout << *closest_element(my_set, 26); // Output: 34
std::cout << *closest_element(my_set, 260); // Output: 256
std::cout << *closest_element(my_set, 620); // Output: 502
Note that std::abs in your code does (almost) nothing: its argument has unsigned type and is always non-negative. But we know that std::set elements are ordered, hence we know that *prev_it <= value <= *it, and no std::abs() is needed.
You could use std::min_element() : as a comperator, give it a lambda that returns the absulute diff e.g.
std::min_element(mySet.begin(), mySet.end(), [searchedElement](const unsigned int a, const unsigned int b) {
return std::abs(searchedElement - a) < std::abs(searchedElement - b);
});
However, I do think this will no longer apply a binary search...
EDIT : Also, as stated in comments below, std::abs(x - y) for unsigned int values may return an unexpectedly large integer when x < y.
The std::set container is suitable for finding adjacent elements, i.e., finding the element that succeeds or precedes a given element. Considering the problem that you are facing:
I am looking for a solution using the standard library, my first solution was to use binary search, but STL's implementation only returns if the element exists.
There is still an approach you can follow without changing your logic: If the element – whose closest element you want to find – does not exist in the set, then you simply insert it in the set (it takes logarithmic time in the size of the set). Next, you find the closest element to this just added element. Finally, remove it from the set when you are done so that the set remains the same as before.
Of course, if the element was already in the set, nothing has to be inserted into or removed from the set. Therefore, you need to keep track of whether or not you added that element.
The following function is an example of the idea elaborated above:
#include <set>
unsigned int find_closest_element(std::set<unsigned int> s, unsigned int val) {
bool remove_elem = false;
auto it = s.find(val);
// does val exist in the set?
if (s.end() == it) {
// element does not exist in the set, insert it
s.insert(val);
it = s.find(val);
remove_elem = true;
}
// find previous and next element
auto prev_it = (it == s.begin()? s.end(): std::prev(it));
auto next_it = std::next(it);
// remove inserted element if applicable
if (remove_elem)
s.erase(it);
unsigned int d1, d2;
d1 = d2 = std::numeric_limits<unsigned int>::max();
if (prev_it != s.end())
d1 = val - *prev_it;
if (next_it != s.end())
d2 = *next_it - val;
return d1 <= d2? *prev_it: *next_it;
}

Removing an object from vector while iterating through it

I'm new to C++ so I'm having trouble figuring out how to best remove an object from a vector while still iterating through it.
Basically, I need to iterate through two vectors. For every item, if the ID's match, I can remove them.
//For every person, check to see if the available bags match:
for(std::vector<Person>::iterator pit = waitingPeopleVector.begin(); pit != waitingPeopleVector.end(); ++pit) {
for(std::vector<Bag>::iterator bit = waitingBagsVector.begin(); bit != waitingBagsVector.end(); ++bit) {
int pId = pit->getId();
int bId = bit->getId();
if(pId == bId){
//a match occurs, remove the bag and person
}
}
}
Working with iterators is a bit confusing, I know I can use the .erase() function on my vectors, but I can't really pass pit or bit. Any help appreciated. Thanks
From the standard:
The iterator returned from a.erase(q) points to the element
immediately following q prior to the element being erased. If no such
element exists, a.end() is returned.
I would use it in something like using the erase method:
std::vector<Person>::iterator pit = waitingPeopleVector.begin();
std::vector<Bag>::iterator bit = waitingBagsVector.begin();
while (pit != waitingPeopleVector.end())
{
bool didit;
while (bit != waitingBagsVector.end())
{
didit = false;
if (pit->getId() == bit->getId() && !didit)
{
bit = waitingBagsVector.erase(bit);
pit = waitingPeopleVector.erase(pit);
didit = true;
}
else
{
++bit;
}
}
if (didit)
continue;
else
++pit;
}
Using the erase-remove idiom will achieve this objective, the below offers an (untested) way using lambdas and <algorithm> functions to remove elements from wPL which have the same ID as wBL. It shouldn't be too much effort to extend this to both lists. Note, we have used std::list instead of std::vector for faster removal.
std::list<Person> wPL;
std::list<Bag> wBL;
//...
wPL.erase(std::remove_if(wPL.begin(), wPL.end(),
[&wBL](auto x) { return std::find_if(wBL.begin(), wBL.end(), [](auto y)
{ return x.getId() == y.getId();); }), wPL.end() };

Iterating through a C++ vector with wrap-around

I have an assignment to iterate through a vector and erase every third number. If it hits the end of the vector, it should continue counting again from the first entry, until only one number remains. The user inputs how many numbers should be in the vector.
I'm having trouble getting used to the difference between vectors and arrays - just last week we had a problem that involved wrapping around an array, which was solved with mod, but I quickly figured out this wouldn't work for vectors.
Here was my idea so far: Iterate through and delete every third entry until the size of the vector is 1.
while (vector.size > 1) {
for(std::vector<int>::iterator i = suitors.begin(); i <= suitors.end(); i++) {
// here, add a case for if it hits the end, start over
if (i = suitors.end()) {
i = suitors.begin();
}
suitors.erase(suitors.at(i) + 2);
}
The problem I'm having is figuring out how to have it start over, as spits out an error when I try to use i in this way.
Any advice or tips to get me on the right path here? I'm beginning to see how versatile vectors are, but they just aren't clicking yet. I'm also not sure if there is a better way to stop it from iterating besides the while loop.
I'd use remove_if to move items in the vector to the end whenever an index variable that is incremented each time reaches 3.
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> v{1,2,3,4,5,6};
unsigned index = 0; // this is the index variable used to remove elements
auto end = v.end(); // point to the current end of the vector
// keep looping until there is only 1 element in the vector
while(std::distance(v.begin(), end) > 1) {
// remove_if will call the predicate for each element
// the predicate simply increments the index each time, and when it reaches
// 3 indicates that element should be removed
// remove_if will move items to the end of the vector and return an
// iterator to the end of the new range, so we'll update the end variable
// with the result
end = std::remove_if(v.begin(), end, [&index](int) {
if(++index == 3) {
// reset the index and indicate this element should be removed
return (index = 0), true;
}
return false;
});
for(auto iter = v.begin(); iter != end; ++iter) {
std::cout << *iter << ' ';
}
std::cout << '\n';
}
// erase all the elements we've removed so far
v.erase(end, v.end());
}
Output:
1 2 4 5
1 2 5
1 5
1
Live demo
The outer while loop I'm assuming means to go as long as the vector has more than one element, but this should be included in the for, not another loop
The syntactic issue is in the if:
if (i = suitors.end())
// ^ should be ==
otherwise you're just assigning end to your iterator
for(std::vector<int>::iterator i = suitors.begin(); suitors.size() > 1; ++i) {
// ^ loop condition changed
if (i == suitors.end()) {
i = suitors.begin();
}
suitors.erase(suitors.at(i) + 2);
}
modifying a container as you iterate through it is dangerous though..