In one of my projects it is necessary to remove certain elements from a std::vector<double> values
. The indices I have to remove are given as a vector of intervals. For example {1,3}
means, that I have to remove indices from 1 to 3 inclusive from values
.
I can assume that the intervals given are mutually exclusive.
The code shown below illustrates, what the desired behavior should look like.
#include <iostream>
#include <vector>
int main(int argc, char** args) {
// Intervals of indices I have to remove from values
std::vector<std::pair<int, int>> intervals = { {1,3},{7,9},{13,13} };
// Vector of arbitrary values.
std::vector<double> values = {4.2,6.4,2.3,3.4,9.1,2.3,0.6,1.2,0.3,0.4,6.4,3.6,1.4,2.5,7.5 }
removeIntervals(values, intervals);
// intervals should contain 4.2,9.1,2.3,0.6,6.4,3.6,1.4,7.5
}
What might be the shortest amount of code necessary to achieve this?
My best solution so far is:
void removeIntervals(std::vector<double>& values, const std::vector < std::pair<int, int>>& intervals) {
std::vector<bool> flags(values.size(), true);
std::vector<double> ret;
for (auto interval : intervals) {
std:fill(flags.begin() + interval.first, flags.begin()+interval.second+1, false);
}
for (auto i = 0; i < values.size(); i++) {
if (flags[i]) ret.push_back(values[i]);
}
values = ret;
}
I can assume, that my intervals are non-overlapping and consecutive. It seems, that it boils down to perform erase from back to front.
void removeIntervals2(std::vector<double>& values, const std::vector < std::pair<int, int>>& intervals) {
auto revIntervals = intervals;
std::reverse(revIntervals.begin(), revIntervals.end());
for (auto interval : revIntervals) {
values.erase(std::begin(values) + interval.first, std::begin(values) + interval.second + 1);
}
}
Since you can assume that the intervals don't overlap and are increasing order, the solution is to start at the back (so that the indexes don't change) and remove each range in turn:
So for the minimum amount of code, which you asked for:
for (auto& it = intervals.rbegin(); it != intervals.rend(); ++it) {
values.erase(values.begin() + it->first, std::next(values.begin() + it->second));
The down side of this is that this will involve lots of shuffling of the vector. Really what you would want to do is swap the last unswapped item at the end of the vector, with the item you want to remove, and then resize when you're finished to cut off the end; but this requires more code.