I am trying to store iterators of a deque in a vector and want to preserve them inside the vector even when I have erased or inserted some elements from or into the deque. Is this possible?
I have the following code:
typedef struct {
int id;
int seedId;
double similarity;
} NODE_SEED_SIM;
typedef std::deque<NODE_SEED_SIM> NodesQueue;
typedef std::deque<NODE_SEED_SIM>::iterator ITRTR;
typedef std::vector<const ITRTR> PointerVec;
void growSegments (CSG_PointCloud *pResult, IntMatrix *WdIndices, NodesQueue *NodesList, IntMatrix *Segments) {
ITRTR nodeslistStart = (*NodesList).begin();
int pointCount = (*WdIndices).size();
int nodeslistSize = (*NodesList).size();
IntVector book(pointCount);
PointerVec pointerList (pointCount); // Vector of ITRTRs
for (int i = 0; i < nodeslistSize; i++) {
book [ (*NodesList)[i].id ] = 1;
pointerList [ (*NodesList)[i].id ] = nodeslistStart + i; // REF: 2
}
while (nodeslistSize > 0) {
int i = 0;
int returnCode = 0;
int nodeId = (*NodesList)[i].id;
int seedId = (*NodesList)[i].seedId;
int n_nbrOfNode = (*WdIndices)[ nodeId ].size();
(*Segments)[ seedId ].push_back ( nodeId );
(*NodesList).erase ( (*NodesList).begin() ); // REF: 3; This erase DOES NOT mess the pointerList
nodeslistSize --;
Point node;
/*
GET ATTRIBUTES OF NODE
*/
for (int j = 0; j < n_nbrOfNode; j++) {
int nborId = (*WdIndices)[nodeId][j];
if (nborId == seedId)
continue;
Point neighbor;
/*
GET ATTRIBUTES OF NEIGHBOUR
*/
double node_nbor_sim = computeSimilarity (neighbor, node);
if (book[nborId] == 1) {
ITRTR curr_node = pointerList[nborId]; // REF: 1
if ( curr_node -> similarity < node_nbor_sim) {
curr_node -> similarity = node_nbor_sim;
NODE_SEED_SIM temp = *curr_node;
(*NodesList).erase (curr_node); // REF: 4; This erase completely messes up the pointerList
returnCode = insertSortNodesList (&temp, NodesList, -1);
}
}
}
}
}
The nodes in the NodesList hold a global ID inside them. However they are stored in NodesList, not according to this global ID but in descending order of their "similarity". So later when I want to get the node from NodesList corresponding to a global ID (nborID in code)[REF: 1] I do it via the "pointerList" where I have previously stored the iterators of the deque but according to the global IDs of the nodes [REF: 2]. My pointerList stays true after the first erase command [REF: 3], but gets messed up in the next erase [REF: 4].
What is wrong here?
Thanks
I am trying to store iterators of a deque in a vector and want to preserve them inside the vector even when I have erased or inserted some elements from or into the deque. Is this possible?
As from the documentation it says
Sorry I'm posting this as an image here, but the formatting is too tedious to replicate in markup!
So the short answer is: NO! I'm afraid you cannot safely store iterators pointing to certain elements stored in a std::deque, while it's changed elsewhere.
Some other relevant Q&A:
Problem with invalidation of STL iterators when calling erase
C++ deque: when iterators are invalidated
If you want to create a vector of iterators then this is what you want to do:
#include<iostream>
#include <deque>
#include <vector>
using namespace std;
int main()
{
deque<int> dq { 1, 2, 3, 4, 5, 6, 7, 8 };
deque<int>::iterator it;
vector<deque<int>::iterator> vit;
for (it = dq.begin(); it != dq.end(); it++)
{
vit.push_back(it);
cout << *it;
}
//note that `it` is a pointer so if you modify the `block` it points in your deque then the value will change.
//If you delete it then you will have a segmenant fault.
//--- Don't Do: while (!dq.empty()) { dq.pop_back(); } ---//
for (size_t i = 0; i < vit.size(); i++)
{
cout << *vit[i];
}
system("pause");
return 0;
}
However, if you want to preserve that value of that iterator after it has been changed/deleted you may want to create a copy of each iterator and store the copy rather than the actual iterator
Related
I am trying to find the best sum with memorization, but when saving the vector pointer inside a map the values keep appending inside the vector and getting the wrong vector.
if I commented out the map insertion it works properly.
saving nullptr is not possible in case trying to save vector inside the map by reference.
std::vector<int> *bestSumV(int target, int nums[], int size) {
static std::map<int, std::vector<int> *> memo;
if (memo.find(target) != memo.end())
return memo.at(target);
if (target == 0)
return new std::vector<int>();
if (target < 0)
return NULL;
std::vector<int> *bestCom = nullptr;
for (int i = 0; i < size; i++) {
int reminder = target - nums[i];
std::vector<int> *reminderResult = bestSumV(reminder, nums, size);
if (reminderResult != NULL) {
reminderResult->push_back(nums[i]);
if (bestCom == nullptr || reminderResult->size() < bestCom->size()) {
bestCom = static_cast<std::vector<int> *>(reminderResult);
}
}
}
// if i commented out the map insertion i am getting the correct value
// and getting a vector of 5 items
memo.insert(std::make_pair(target, std::move(bestCom)));
return bestCom;
}
void runHowbestTest() {
int testArray[] = {5, 4, 2};
std::vector<int> *bestSum25 = bestSumV(25, testArray, 3);
for (int i = 0; i < bestSum25->size(); i++) {
std::cout << "the items " << bestSum25->at(i) << std::endl;
}
}
bestCom is a std::vector<int> * don't std::move it. It's pointless, and makes the code hard to read.
reminderResult is already a std::vector<int> *, no need to static_cast<std::vector<int> *> it.
On this line
memo.insert(std::make_pair(target, std::move(bestCom)));
bestCom might be null. When that happens,
if (memo.find(target) != memo.end())
return memo.at(target);
will return and bypass the function logic. You need:
if (memo.find(target) != memo.end() && memo.at(target))
return memo.at(target);
And, most likely, the real issue:
// returns a memoized value
std::vector<int> *reminderResult = bestSumV(reminder, nums, size);
...
// modifies it.
reminderResult->push_back(nums[i]);
You cannot modify a memoized value and expect it to be valid.
Using objects instead of pointers fixes the issue: https://godbolt.org/z/v35oT4 . But I make no claims about what it does to performance.
This is a2.hpp, and is the program that can be edited, as far as I know the code is correct, just too slow. I am honestly lost here, I know my for loops are probably whats slowing me down so much, maybe use an iterator?
// <algorithm>, <list>, <vector>
// YOU CAN CHANGE/EDIT ANY CODE IN THIS FILE AS LONG AS SEMANTICS IS UNCHANGED
#include <algorithm>
#include <list>
#include <vector>
class key_value_sequences {
private:
std::list<std::vector<int>> seq;
std::vector<std::vector<int>> keyref;
public:
// YOU SHOULD USE C++ CONTAINERS TO AVOID RAW POINTERS
// IF YOU DECIDE TO USE POINTERS, MAKE SURE THAT YOU MANAGE MEMORY PROPERLY
// IMPLEMENT ME: SHOULD RETURN SIZE OF A SEQUENCE FOR GIVEN KEY
// IF NO SEQUENCE EXISTS FOR A GIVEN KEY RETURN 0
int size(int key) const;
// IMPLEMENT ME: SHOULD RETURN POINTER TO A SEQUENCE FOR GIVEN KEY
// IF NO SEQUENCE EXISTS FOR A GIVEN KEY RETURN nullptr
const int* data(int key) const;
// IMPLEMENT ME: INSERT VALUE INTO A SEQUENCE IDENTIFIED BY GIVEN KEY
void insert(int key, int value);
}; // class key_value_sequences
int key_value_sequences::size(int key) const {
//checks if the key is invalid or the count vector is empty.
if(key<0 || keyref[key].empty()) return 0;
// sub tract 1 because the first element is the key to access the count
return keyref[key].size() -1;
}
const int* key_value_sequences::data(int key) const {
//checks if key index or ref vector is invalid
if(key<0 || keyref.size() < static_cast<unsigned int>(key+1)) {
return nullptr;
}
// ->at(1) accesses the count (skipping the key) with a pointer
return &keyref[key].at(1);
}
void key_value_sequences::insert(int key, int value) {
//checks if key is valid and if the count vector needs to be resized
if(key>=0 && keyref.size() < static_cast<unsigned int>(key+1)) {
keyref.resize(key+1);
std::vector<int> val;
seq.push_back(val);
seq.back().push_back(key);
seq.back().push_back(value);
keyref[key] = seq.back();
}
//the index is already valid
else if(key >=0) keyref[key].push_back(value);
}
#endif // A2_HPP
This is a2.cpp, this just tests the functionality of a2.hpp, this code cannot be changed
// DO NOT EDIT THIS FILE !!!
// YOUR CODE MUST BE CONTAINED IN a2.hpp ONLY
#include <iostream>
#include "a2.hpp"
int main(int argc, char* argv[]) {
key_value_sequences A;
{
key_value_sequences T;
// k will be our key
for (int k = 0; k < 10; ++k) { //the actual tests will have way more than 10 sequences.
// v is our value
// here we are creating 10 sequences:
// key = 0, sequence = (0)
// key = 1, sequence = (0 1)
// key = 2, sequence = (0 1 2)
// ...
// key = 9, sequence = (0 1 2 3 4 5 6 7 8 9)
for (int v = 0; v < k + 1; ++v) T.insert(k, v);
}
T = T;
key_value_sequences V = T;
A = V;
}
std::vector<int> ref;
if (A.size(-1) != 0) {
std::cout << "fail" << std::endl;
return -1;
}
for (int k = 0; k < 10; ++k) {
if (A.size(k) != k + 1) {
std::cout << "fail";
return -1;
} else {
ref.clear();
for (int v = 0; v < k + 1; ++v) ref.push_back(v);
if (!std::equal(ref.begin(), ref.end(), A.data(k))) {
std::cout << "fail 3 " << A.data(k) << " " << ref[k];
return -1;
}
}
}
std::cout << "pass" << std::endl;
return 0;
} // main
If anyone could help me improve my codes efficiency I would really appreciate it, thanks.
First, I'm not convinced your code is correct. In insert, if they key is valid you create a new vector and insert it into sequence. Sounds wrong, as that should only happen if you have a new key, but if your tests pass it might be fine.
Performance wise:
Avoid std::list. Linked lists have terrible performance on today's hardware because they break pipelineing, caching and pre-fetching. Always use std::vector instead. If the payload is really big and you are worried about copies use std::vector<std::unique_ptr<T>>
Try to avoid copying vectors. In your code you have keyref[key] = seq.back() which copies the vector, but should be fine since it's only one element.
Otherwise there's no obvious performance problems. Try to benchmark and profile your program and see where the slow parts are. Usually there's one or two places that you need to optimize and get great performance. If it's still too slow, ask another question where you post your results so that we can better understand the problem.
I will join Sorin in saying don't use std::list if avoidable.
So you use key as direct index, where does it say it is none-negative? where does it say its less than 100000000?
void key_value_sequences::insert(int key, int value) {
//checks if key is valid and if the count vector needs to be resized
if(key>=0 && keyref.size() < static_cast<unsigned int>(key+1)) {
keyref.resize(key+1); // could be large
std::vector<int> val; // don't need this temporary.
seq.push_back(val); // seq is useless?
seq.back().push_back(key);
seq.back().push_back(value);
keyref[key] = seq.back(); // we now have 100000000-1 empty indexes
}
//the index is already valid
else if(key >=0) keyref[key].push_back(value);
}
Can it be done faster? depending on your key range yes it can. You will need to implement a flat_map or hash_map.
C++11 concept code for a flat_map version.
// effectively a binary search
auto key_value_sequences::find_it(int key) { // type should be iterator
return std::lower_bound(keyref.begin(), keyref.end(), [key](const auto& check){
return check[0] < key; // key is 0-element
});
}
void key_value_sequences::insert(int key, int value) {
auto found = find_it(key);
// at the end or not found
if (found == keyref.end() || found->front() != key) {
found = keyref.emplace(found, key); // add entry
}
found->emplace_back(value); // update entry, whether new or old.
}
const int* key_value_sequences::data(int key) const {
//checks if key index or ref vector is invalid
auto found = find_it(key);
if (found == keyref.end())
return nullptr;
// ->at(1) accesses the count (skipping the key) with a pointer
return found->at(1);
}
(hope I got that right ...)
I am wondering what the fast swap method is in C++ containers such as list and vector, because I haven't found any builtin swap functions yet.FYI, I want to swap object rather than the whole list.
For example, assume we have such int sequence 3 2 4 5 and they are stored in a list container(stl), and I want to swap 2 and 4. Here is the dumb method that I came up with:
list<int> numbers;
numbers.push_back(3);
numbers.push_back(2);
numbers.push_back(4);
numbers.push_back(5);
list<int>::iterator item;
item=numbers.begin();
advance(item,2);
int key = *item;
advance(item,-1);
numbers.insert(item,key);
advance(item,1);
numbers.erase(item);
So, briefly speaking, what I am doing here is just "Copy, insert, and remove", and the reason why I do this is that I heard list container is very efficient for inserting and removing elements,but I am pretty sure there should be better algorithms.In addition, I also heard there exists a constant time swap method related to pointers, so anyone knows anything about it?
Thanks for helping out.
You want std::swap:
list<int>::iterator item1 = numbers.begin();
++item1;
list<int>::iterator item2 = item1;
++item2;
std::swap(*item1, *item2);
Use iter_swap to swap the elements pointed at by two iterators to the list. This swaps the data, rather than the nodes, but it's easy.
#include <list>
#include <iostream>
int main() {
std::list<int> numbers;
numbers.push_back(3);
numbers.push_back(2);
numbers.push_back(4);
numbers.push_back(5);
auto first = std::next(numbers.begin(), 2);
auto second = std::next(numbers.begin(), 1);
std::iter_swap(first, second);
for(int& v : numbers)
std::cout << v << ' ';
}
http://coliru.stacked-crooked.com/view?id=a89b3b1ae9400367b6ff194d1b504e58-f674c1a6d04c632b71a62362c0ccfc51
If you want to swap nodes rather than elements, you can use list::splice, though it's a little tricker:
int main() {
std::list<int> numbers;
numbers.push_back(3);
numbers.push_back(2);
numbers.push_back(4);
numbers.push_back(5);
std::list<int> temporary;
auto move_from = std::next(numbers.begin(), 2);
temporary.splice(temporary.begin(), numbers, move_from, std::next(move_from));
auto move_to = std::next(numbers.begin(), 1);
numbers.splice(move_to, temporary);
for(int& v : numbers)
std::cout << v << ' ';
}
It seems you may be looking for a way to move around nodes within the list, without copying actual elements. You could do this with list::splice. Nothing like that is possible for vector, of course, which is not node-based.
Something like this:
list<int>::iterator to = numbers.begin();
++to;
list<int>::iterator which = to;
++which;
numbers.splice(to, numbers, which);
How about using swap?
using std::swap;
swap(numbers[1], numbers[2]);
which will use std:swap or ADL to determine a proper swap-function if there is one defined for the arguments.
As #Mooing Duck correctly pointed out std::list requires you to use iterators.
std::iter_swap(numbers.begin()+1, numbers.begin()+2);
You can also use
using std::swap;
std::list<int>::iterator item(numbers.begin());
std::advance(item, 1);
std::list<int>::iterator other(item);
std::advance(other, 1);
swap(*item, *other);
or
using std::swap;
swap(*std::next(numbers.begin(), 1), *std::next(numbers.begin(), 2));
or
std::iter_swap(std::next(numbers.begin(), 1), std::next(numbers.begin(), 2));
With std::swap(),
int reverse(std::list<int>& list){
int exchange = 0;
int move = 0;
int distance_lo = 0;
int distance_hi = 0;
std::list<int>::iterator it_lo = list.begin();
std::list<int>::iterator it_hi = --list.end();
while (1) {
it_lo = list.begin();
it_hi = --list.end();
std::advance(it_lo, move);
std::advance(it_hi, -move);
distance_lo = std::distance(list.begin(), it_lo);
distance_hi = std::distance(list.begin(), it_hi);
if (distance_lo < distance_hi) {
std::swap(*it_lo, *it_hi);
exchange++;
} else {
break;
}
move++;
}
return exchange; }
With std::list::splice(),
int reverse(std::list<int>& list) {
int exchange = 0;
int move = 0;
int distance_lo = 0;
int distance_hi = 0;
std::list<int>::iterator it_lo = list.begin();
std::list<int>::iterator it_hi = --list.end();
while (1) {
it_lo = list.begin();
it_hi = --list.end();
std::advance(it_lo, move);
std::advance(it_hi, -move);
distance_lo = std::distance(list.begin(), it_lo);
distance_hi = std::distance(list.begin(), it_hi);
if (distance_lo < distance_hi) {
std::list<int> tmp;
tmp.splice(tmp.begin(), list, it_lo);
tmp.splice(std::next(tmp.begin(),1), list, it_hi);
it_lo = list.begin();
it_hi = --list.end();
std::advance(it_lo, move); //insert before it_lo
std::advance(it_hi, -move);
std::advance(it_hi, 1); //insert after it_hi
list.splice(it_lo, tmp, std::next(tmp.begin(),1));
list.splice(it_hi, tmp, tmp.begin());
exchange++;
} else {
break;
}
move++;
}
return exchange; }
Hope it could help you :)
Eventually if I am trying to delete all the elements of a vector associated with a key, I am encountering a segmentation fault. My intended output is new b new c new d new a, but i am getting new b new c new d segmentation fault.
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
int main ()
{
map<char,vector<char> > mmap; //multimap
char mychar[] = { 'b','c', 'd'};
vector<char> vec (mychar,mychar+3);
vector<char> newvec;
mmap.insert (pair<char,vector<char> >('a',vec)); //insert to multimap
mmap.insert (pair<char,vector<char> >('b',vector<char>()));
mmap.insert (pair<char,vector<char> >('c',vector<char>()));
mmap.insert (pair<char,vector<char> >('d',vector<char>()));
vector<char>::iterator veciter;
map<char,vector<char> >::iterator mapiter;
for(int i=0;i<6;i++)
{
for ( mapiter = mmap.begin(); mapiter != mmap.end(); ++mapiter)
{
//if elements associated with vector of a key are empty the store the key in a new vector
if(mapiter->second.empty())
{
newvec.push_back (mapiter->first);
mmap.erase(mapiter);
}
else
{
for (veciter = mapiter->second.begin(); veciter != mapiter->second.end(); ++veciter)
{
//if an element of a vector of key is found in new vector, erase the element
if (find(newvec.begin(), newvec.end(), *veciter)!=newvec.end())
{
mapiter->second.erase(veciter);
}
}
}
// to display values of new vector
for (unsigned i=0; i<newvec.size(); ++i)
cout << "new " << newvec[i]<<' ';
cout << '\n';
}
}
return 0;
}
When you pass an iterator to a container's erase function, that iterator becomes invalidated. You need to account for that. Assuming, for some reason, that neither std::remove nor std::remove_if will work for your situation, the standard idiom goes like this:
for (it = container.begin(); it != container.end(); /* no increment here */)
{
if (should_be_removed(*it))
{
// possibly other operations involving the element we are about to remove
it = container.erase(it);
}
else
{
// possibly other operations involving the element we chose not to remove
++it;
}
}
When we erase an element, we capture the return value of the erase operation, which is the next iterator. Otherwise, we increment. Note the space where I left room for other possible operations. If there are no other operations, you should be able to just use std::remove or std::remove_if, combined with the container's range erase function (the one that takes two iterators).
I've a std::vector<int> and I need to remove all elements at given indexes (the vector usually has high dimensionality). I would like to know, which is the most efficient way to do such an operation having in mind that the order of the original vector should be preserved.
Although, I found related posts on this issue, some of them needed to remove one single element or multiple elements where the remove-erase idiom seemed to be a good solution.
In my case, however, I need to delete multiple elements and since I'm using indexes instead of direct values, the remove-erase idiom can't be applied, right?
My code is given below and I would like to know if it's possible to do better than that in terms of efficiency?
bool find_element(const vector<int> & vMyVect, int nElem){
return (std::find(vMyVect.begin(), vMyVect.end(), nElem)!=vMyVect.end()) ? true : false;
}
void remove_elements(){
srand ( time(NULL) );
int nSize = 20;
std::vector<int> vMyValues;
for(int i = 0; i < nSize; ++i){
vMyValues.push_back(i);
}
int nRandIdx;
std::vector<int> vMyIndexes;
for(int i = 0; i < 6; ++i){
nRandIdx = rand() % nSize;
vMyIndexes.push_back(nRandIdx);
}
std::vector<int> vMyResult;
for(int i=0; i < (int)vMyValues.size(); i++){
if(!find_element(vMyIndexes,i)){
vMyResult.push_back(vMyValues[i]);
}
}
}
I think it could be more efficient, if you just just sort your indices and then delete those elements from your vector from the highest to the lowest. Deleting the highest index on a list will not invalidate the lower indices you want to delete, because only the elements higher than the deleted ones change their index.
If it is really more efficient will depend on how fast the sorting is. One more pro about this solultion is, that you don't need a copy of your value vector, you can work directly on the original vector. code should look something like this:
... fill up the vectors ...
sort (vMyIndexes.begin(), vMyIndexes.end());
for(int i=vMyIndexes.size() - 1; i >= 0; i--){
vMyValues.erase(vMyValues.begin() + vMyIndexes[i])
}
to avoid moving the same elements many times, we can move them by ranges between deleted indexes
// fill vMyIndexes, take care about duplicated values
vMyIndexes.push_back(-1); // to handle range from 0 to the first index to remove
vMyIndexes.push_back(vMyValues.size()); // to handle range from the last index to remove and to the end of values
std::sort(vMyIndexes.begin(), vMyIndexes.end());
std::vector<int>::iterator last = vMyValues.begin();
for (size_t i = 1; i != vMyIndexes.size(); ++i) {
size_t range_begin = vMyIndexes[i - 1] + 1;
size_t range_end = vMyIndexes[i];
std::copy(vMyValues.begin() + range_begin, vMyValues.begin() + range_end, last);
last += range_end - range_begin;
}
vMyValues.erase(last, vMyValues.end());
P.S. fixed a bug, thanks to Steve Jessop that patiently tried to show me it
Erase-remove multiple elements at given indices
Update: after the feedback on performance from #kory, I've modified the algorithm not to use flagging and move/copy elements in chunks (not one-by-one).
Notes:
indices need to be sorted and unique
uses std::move (replace with std::copy for c++98):
Github
Live example
Code:
template <class ForwardIt, class SortUniqIndsFwdIt>
inline ForwardIt remove_at(
ForwardIt first,
ForwardIt last,
SortUniqIndsFwdIt ii_first,
SortUniqIndsFwdIt ii_last)
{
if(ii_first == ii_last) // no indices-to-remove are given
return last;
typedef typename std::iterator_traits<ForwardIt>::difference_type diff_t;
typedef typename std::iterator_traits<SortUniqIndsFwdIt>::value_type ind_t;
ForwardIt destination = first + static_cast<diff_t>(*ii_first);
while(ii_first != ii_last)
{
// advance to an index after a chunk of elements-to-keep
for(ind_t cur = *ii_first++; ii_first != ii_last; ++ii_first)
{
const ind_t nxt = *ii_first;
if(nxt - cur > 1)
break;
cur = nxt;
}
// move the chunk of elements-to-keep to new destination
const ForwardIt source_first =
first + static_cast<diff_t>(*(ii_first - 1)) + 1;
const ForwardIt source_last =
ii_first != ii_last ? first + static_cast<diff_t>(*ii_first) : last;
std::move(source_first, source_last, destination);
// std::copy(source_first, source_last, destination) // c++98 version
destination += source_last - source_first;
}
return destination;
}
Usage example:
std::vector<int> v = /*...*/; // vector to remove elements from
std::vector<int> ii = /*...*/; // indices of elements to be removed
// prepare indices
std::sort(ii.begin(), ii.end());
ii.erase(std::unique(ii.begin(), ii.end()), ii.end());
// remove elements at indices
v.erase(remove_at(v.begin(), v.end(), ii.begin(), ii.end()), v.end());
What you can do is split the vector (actually any non-associative container) in two
groups, one corresponding to the indices to be erased and one containing the rest.
template<typename Cont, typename It>
auto ToggleIndices(Cont &cont, It beg, It end) -> decltype(std::end(cont))
{
int helpIndx(0);
return std::stable_partition(std::begin(cont), std::end(cont),
[&](typename Cont::value_type const& val) -> bool {
return std::find(beg, end, helpIndx++) != end;
});
}
you can then delete from (or up to) the split point to erase (keep only)
the elements corresponding to the indices
std::vector<int> v;
v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
int ar[] = { 2, 0, 4 };
v.erase(ToggleIndices(v, std::begin(ar), std::end(ar)), v.end());
If the 'keep only by index' operation is not needed you can use remove_if insted of stable_partition (O(n) vs O(nlogn) complexity)
To work for C arrays as containers the lambda function should be
[&](decltype(*(std::begin(cont))) const& val) -> bool
{ return std::find(beg, end, helpIndx++) != end; }
but then the .erase() method is no longer an option
If you want to ensure that every element is only moved once, you can simply iterate through each element, copy those that are to remain into a new, second container, do not copy the ones you wish to remove, and then delete the old container and replace it with the new one :)
This is an algorithm based on Andriy Tylychko's answer so that this can make it easier and faster to use the answer, without having to pick it apart. It also removes the need to have -1 at the beginning of the indices list and a number of items at the end. Also some debugging code to make sure the indices are valid (sorted and valid index into items).
template <typename Items_it, typename Indices_it>
auto remove_indices(
Items_it items_begin, Items_it items_end
, Indices_it indices_begin, Indices_it indices_end
)
{
static_assert(
std::is_same_v<std::random_access_iterator_tag
, typename std::iterator_traits<Items_it>::iterator_category>
, "Can't remove items this way unless Items_it is a random access iterator");
size_t indices_size = std::distance(indices_begin, indices_end);
size_t items_size = std::distance(items_begin, items_end);
if (indices_size == 0) {
// Nothing to erase
return items_end;
}
// Debug check to see if the indices are already sorted and are less than
// size of items.
assert(indices_begin[0] < items_size);
assert(std::is_sorted(indices_begin, indices_end));
auto last = items_begin;
auto shift = [&last, &items_begin](size_t range_begin, size_t range_end) {
std::copy(items_begin + range_begin, items_begin + range_end, last);
last += range_end - range_begin;
};
size_t last_index = -1;
for (size_t i = 0; i != indices_size; ++i) {
shift(last_index + 1, indices_begin[i]);
last_index = indices_begin[i];
}
shift(last_index + 1, items_size);
return last;
}
Here is an example of usage:
template <typename T>
std::ostream& operator<<(std::ostream& os, std::vector<T>& v)
{
for (auto i : v) {
os << i << " ";
}
os << std::endl;
return os;
}
int main()
{
using std::begin;
using std::end;
std::vector<int> items = { 1, 3, 6, 8, 13, 17 };
std::vector<int> indices = { 0, 1, 2, 3, 4 };
std::cout << items;
items.erase(
remove_indices(begin(items), end(items), begin(indices), end(indices))
, std::end(items)
);
std::cout << items;
return 0;
}
Output:
1 3 6 8 13 17
17
The headers required are:
#include <iterator>
#include <vector>
#include <iostream> // only needed for output
#include <cassert>
#include <type_traits>
And a Demo can be found on godbolt.org.