How to search in a map/multimap starting from specific position - c++

I want to search in a map/multimap but not all of it. Instead I want to start in a specific position.
In the following example I want to find the two first numbers that sum b. And return their value.
multimap<int, int> a;
a.insert(make_pair(2, 0));
a.insert(make_pair(2, 1));
a.insert(make_pair(5, 2));
a.insert(make_pair(8, 3));
int b = 4;
for(auto it = a.begin(); it != a.end(); ++it) {
auto it2 = a.find(b - it->first); //Is there an equivalent that starts from "it+1"?
if(it2 != a.end()) {
cout << it->second << ", " << it2->second << endl;
break;
}
}
output:
0, 0
desired output:
0, 1
Is it possible to achieve specific position search in a map?

How to search in a map starting from specific position
You could use std::find. But this is not ideal, since it has linear complexity compared to logarithmic complexity of a map lookup. The interface of std::map doesn't support such operation for lookups.
If you need such operation, then you need to use another data structure. It should be possible to implement by augmenting a (balanced) search tree with a parent node pointer. The downside is of course increased memory use and constant overhead on operations that modify the tree structure.
not from the beginning to the end.
Map look ups do not start from "the beginning" of the range. They start from the root of the tree.

If you're using an ordered map (which it sounds like you are), then it already does binary search with std::find. This function returns an iterator type, so assuming you were looking for the value of some key x, then consider the following lines:
std::map<char,int> mymap;
mymap['x'] = 24;
std::map<char,int>::iterator itr = mymap.find('x');
std::cout << "x=" << itr->second << std::endl;
The reason your code wasn't compiling was likely because you tried to return a pair iterator, which won't exactly print to output all that well. Instead, calling itr->second allows you to retrieve the value associated with the desired key.

Related

Is there a way to make selecting a specific variable more efficient?

So I'm writing a program that has 9 different mazes in it stored in 2d arrays all filled with hard coded values. When the player chooses the maze, I want to copy the hard coded values from the maze selected into the 2d array of the active maze. When I wrote it out I did it in the most straightforward way possible as you can see below. Then I wanted to maze it better as it seems... bloated. A switch case wouldn't reduce the amount of lines, so I wanted to make some way to immediately put the int mazeSelection variable into the variable name of the maze selected. But it seems you can't alter a variable name during runtime, nor use a string variable to represent the name of another variable. For example string mazenumber = "maze" + tostring(mazeSelection); then doing mazenumber[11][11] doesn't work, but that's the basic idea of what I want to do.
So the upshot is, is there a way to make this code more efficient?
if(mazeSelection == 1)
maze[11][11] = maze1[11][11];
if(mazeSelection == 2)
maze[11][11] = maze2[11][11];
if(mazeSelection == 3)
maze[11][11] = maze3[11][11];
if(mazeSelection == 4)
maze[11][11] = maze4[11][11];
if(mazeSelection == 5)
maze[11][11] = maze5[11][11];
if(mazeSelection == 6)
maze[11][11] = maze6[11][11];
if(mazeSelection == 7)
maze[11][11] = maze7[11][11];
if(mazeSelection == 8)
maze[11][11] = maze8[11][11];
if(mazeSelection == 9)
maze[11][11] = maze9[11][11];
So your question is lacking in detail, but lets assume you have this
int maze[11][11], maze1[11][11]; // etc
Then the first thing to say is that
maze[11][11] = maze1[11][11];
does not copy your maze. It's a very common beginner misunderstanding that you can refer to a whole array this way, but maze1[11][11] just refers to one element of the maze at coordinates (11,11) not to the whole maze. And worse since the size of the array is 11 by 11, that element doesn't actually exist, so the code is just an error. There is (surprisingly) no simple way to copy an array in C++.
The simplest suggestion (thanks to #molbdnilo) is to put your maze inside a struct.
struct Maze
{
int tiles[11][11];
};
Maze maze, maze1; // etc
Now structs can be copied in the usual way, so
maze = maze1;
is legal code and does copy the maze.
Then you can go further and make an array of mazes, and write this simple code
Maze selected_maze, all_mazes[10];
selected_maze = all_mazes[mazeSelection];
Easiest way for your case is to store needed to choose values in vector and then just index that vector. I.e. instead of writing
if (x == 0)
y = z0;
else if (x == 1)
y = z1;
else if (x == 2)
y = z2;
you do
static vector<ValueT> values = {z0, z1, z2, ValueT()/*no answer for 3*/, z4, z5};
if (x < values.size())
y = values[x];
else
y = ValueT(); // No value found!
Note that above solution works good if keys to check (inside if condition) cover densely whole vector's range [0;size), if some values are absent you may store in corresponding vector's slots special values signifying that there is no answer.
If keys-space is to sparse then there will be too many no-value elements in vector and this solution may be to memory-wasteful, then next other solutions will do. But this vector-solution is the fastest regarding speed of getting right value by given key.
In more difficult cases, when you need to have arbitrary expression inside if condition or when you need to run arbitrary code in if body, then you need to use more advanced solutions like those that I've just coded into code below (I've coded all cases in growing complexity order). All these solutions are implementing fastest way to make if/then choice.
I'll explain code a bit.
When if condition just checks for equality to number in range [0;size) and result is just a value then we use vector. vector's values can be plain objects to return, or functional objects to be run (in case of complex handlers that are inside if-body). This choice works in constant O(1) time, i.e. very fast (see Time Complexity).
If keys to compare to are sparse (e.g. numbers 10, 20, 30, 40) then we use unordered_map or map, map can be used for any keys that are orderable (for map) or support equality and hashable (for unordered_map). This solution works in O(1) time for unordered_map, i.e. very fast (but constant may be not very small), use unordered_map if you have dozens of if-cases. For map it works in O(log(N)) time (N is number of handlers/if-bodies), so is also very fast, use it for cases below dozen. map is faster than unordered_map for small number of cases.
For the most complex case when if-conditions are complex expressions and if-bodies are also complex code-blocks, i.e. when we have func-func mapping, can be also solved fast in logarithmic time (O(log(N))) but only for the case if all if-cases can be ordered (sorted) in such a way that for each current if-case we can definitely tell if the correct matching case probably (if exists at all) lies in handlers to the left from current (flag -1) or that current case is matching one (flag 0) or that correct matching case probably (if exists at all) lies in handlers to the right from current. In other words all handlers can be ordered in one definite way for all possible input arguments (of if-condition expression-func). In this case we just do a logarithmic-time Binary Search using std::lower_bound(...).
So all recommendations are:
If keys are non-negative integers (or keys can be mapped to such integers via some simple function) and if this integers space is not too sparse (otherwise vector-solution is memory-wasteful) then use std::vector for mapping. Fetching from vector by index is O(1) time with small constant time (several CPU instructions), i.e. very fast.
If there are very many keys (more than hundreds) and keys are equality-comparable and hash-able then use std::unordered_map. Fetching time by key is O(1) for unordered time, but with not-so-small constant time (hundred of CPU instructions), i.e. also very fast and fetch time doesn't grow with number of map elements.
If keys are not too many (below hundred) and keys are fully-orderable (i.e. can be sorted) then use std::map. It has O(log(N)) time with small constant, i.e. also very fast.
If there are no keys, i.e. if-conditions are complex expressions and there are many (more than dozen) of if cases then use std::vector of sorted pairs of functions representing a pair of (if_condition_code_matcher, if_body_code). Searching matching cases would need O(log(N)) number of if-condition-code evaluations, i.e. also very fast.
If there are very few (below 5-6) if-cases or if you don't need speed or when if-cases-handlers can't be sorted regarding all arguments or rules 1-5 don't apply or you simply don't want any complex solutions then use just plain set of ifs.
Rules 1-5 are all about different ways of fast finding matching if-case. Regarding values - all structures above can store any type of value. So:
Store regular objects as structure's values (int, string, or any class object) if your if-cases where just returning some data without any code-computation, like in your case. Just return this value after obtaining structure's slot by key.
Store functions as structure's values if your if-bodies contains complex code. After fetching by key just run this function-value as a handler.
Also in case of keys being integers (or map-able to them) like in case 1 you can also use old-good switch-case. Clever compilers optimize code in such a way that they use only goto commands for jumping to matching if-body. But for this case you need to have all cases within switch being ordered, and also all values for range [0;size) should be covered in switch's cases. But such optimization is not guaranteed, compiler still may do regular sequential if-condition-trying. Hence vector solution is the only guaranteed optimization.
Code below can also be run online here.
#include <unordered_map>
#include <functional>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include <utility>
#include <algorithm>
using namespace std;
#define ASSERT_MSG(cond, msg) { if (!(cond)) throw runtime_error("Failed assertion [" #cond "]! Msg: " + string(msg)); }
typedef string ValT;
// Makes choice in fixed O(1) time.
// Suitable only for having possible mapping for all keys in range [0;size).
ValT const & HandleValToValVec(size_t i) {
static std::vector<ValT> handlers = {"zero", "one", "two", "three"};
if (i < handlers.size()) {
ValT const & val = handlers[i];
cout << "Key " << i << " was mapped to \"" << val << "\"" << endl;
return val;
} else {
cout << "Key " << i << " has no mapping!" << endl;
static ValT null_val;
return null_val;
}
}
typedef size_t KeyT;
// Makes choice in fixed O(1) time.
// Suitable for any comparable keys or if keys space is sparse (not covering range [0;size)).
ValT const & HandleValToValMap(KeyT const & key) {
static std::unordered_map<KeyT, ValT> handlers = {{10, "ten"}, {20, "twenty"}};
auto it = handlers.find(key);
if (it == handlers.end()) {
cout << "Key " << key << " has no mapping!" << endl;
static ValT null_val;
return null_val;
} else {
cout << "Key " << key << " was mapped to \"" << it->second << "\"" << endl;
return it->second;
}
}
// Makes choice in fixed O(1) time.
void HandleValToFunc(KeyT const & key) {
// Handlers containing any arbitrary code, "static" here is important to re-create array only once.
static std::unordered_map< KeyT, function<void()> > handlers = {
{KeyT(10), [&](){
cout << "Chosen Key 10" << endl;
}},
{KeyT(15), [&](){
cout << "Chosen Key 15" << endl;
}},
};
auto it = handlers.find(key);
if (it == handlers.end())
cout << "No Handler for Key " << key << endl;
else
it->second();
}
typedef string ArgT;
// Makes choice in logarithmic O(log(N)) time, where N is number of handlers.
void HandleFuncToFunc(ArgT const & arg0) {
// Handlers containing any arbitrary code, "static" here is important to re-create array only once.
// First function in handlers's pair should return -1 if matching handler probably lies to the left,
// 0 if this handler is matching, 1 if probably lies to the right.
static std::vector< pair< function<int(ArgT const &)>, function<void()> > > handlers = {
{[](ArgT const & arg0)->int{
return arg0.size() < 3 ? -1 : arg0.size() < 5 ? 0 : 1;
}, [&](){
cout << "Chosen String with len within range [3;5)." << endl;
}},
{[](ArgT const & arg0)->int{
return arg0.size() < 6 ? -1 : arg0.size() < 8 ? 0 : 1;
}, [&](){
cout << "Chosen String with len within range [6;8)." << endl;
}},
};
auto it = std::lower_bound(handlers.begin(), handlers.end(), arg0, [](auto const & handler, ArgT const & arg0) {
return handler.first(arg0) > 0;
});
if (it == handlers.end() || it->first(arg0) != 0)
cout << "No Handler for String \"" << arg0 << "\"" << endl;
else
it->second();
}
int main() {
try {
HandleValToValVec(0); HandleValToValVec(3); HandleValToValVec(5);
HandleValToValMap(10); HandleValToValMap(20); HandleValToValMap(30);
HandleValToFunc(10); HandleValToFunc(15); HandleValToFunc(20);
HandleFuncToFunc("ab"); HandleFuncToFunc("abcd"); HandleFuncToFunc("abcde"); HandleFuncToFunc("abcdef"); HandleFuncToFunc("abcdefgh");
return 0;
} catch (exception const & ex) {
cerr << "Exception: " << ex.what() << endl;
return -1;
}
}

list<pair<float,float>> iterating through a list that holds pairs?

As a part of runtime analysis I've got a small game that after calculating every Frame puts a new element in this list:
typedef std::list<std::pair<float, float>> PairList;
PairList Frames; //in pair: index 0 = elapsed time, index 1 = frames
The txt file is later used to draw a graph.
I decided to use a list, because while playing I do not need to process data held in the list and I think lists are the fastest containers when it comes to only adding or deleting items. As a next step I want to write the frames in an external txt file.
void WriteStats(PairList &pairList)
{
// open a file in write mode.
std::ofstream outfile;
outfile.open("afile.dat");
PairList::iterator itBegin = pairList.begin();
PairList::iterator itEnd = pairList.end();
for (auto it = itBegin; it != itEnd; ++it)
{
outfile << *it.first << "\t" << *it.second;
}
outfile.close();
}
With normal lists the pointer to "it" should return the item right?
Except visual studio says pair<float, float>* does not have a member called first
How do I want to do it then, when access via my iterator does not work? Is it because I pass in the reference to the list?
*it.first is parsed as *(it.first).
You need (*it).first or, better yet it->first.
Or, even better yet use range for:
for (auto& elem : pairList)
{
float a = elem.first;
}
I decided to use a list, because [...] I think lists are the fastest containers when it comes to only adding or deleting items.
The first go-to container should be std::vector. In practice it will outperform std::list even on algorithms that on paper should be faster on std::list because of cache locality. So I would test your theory with a good-ol benchmarking if performance is a concern.
The issue is one of operator precedence. Specifically, the member access operator '.' has higher precedence than indirection '*' so *it.first is effectively parsed as...
*(it.first)
Hence the warning. Instead use...
it->first
Use a range-based for loop instead of messing with iterators:
void WriteStats(const PairList &pairList)
{
// open a file in write mode.
std::ofstream outfile("afile.dat");
for (const auto &elem : pairList) {
outfile << elem.first << "\t" << elem.second << '\n';
}
}

Finding Common (or repeated) keys in unsorted_multimap

Any idea about how to get common keys from large set of unsorted_multimap ??? I use file_name(string) as a key and its size(int) as a value. Basically I am scanning a directory for searching duplicate files using boost and holding entry of each file in unsorted_multimap. Once this map is ready I need to output common keys(file_name) and there sizes as a list of duplicate files.
How to find common keys of an unsorted_multimap ?
The following code searches for a specific filename, and iterates through all elements with the same key:
std::unordered_multimap<std::string, int> mymulti; // key: filename, value: size
//... fill the multimap
for (auto x = mymulti.find("fileb"); x != mymulti.end() && x->first == "fileb"; x++) {
std::cout << x->second << " "; // do something !
}
std::cout << "}\n"; // end something !
How to iterate through an unsorted_multimap, goupring processing by common keys ?
The following code iterates trhough the whole map, and for eacuh key, processes in a subloop the related values:
for (auto i = mymulti.begin(); i != mymulti.end(); ) { // iterate through multimap
auto group = i->first; // start a new group
std::cout << group << "={"; // start doing something for the group
do {
std::cout << i->second << " "; // do something for every values of the group
} while (++i != mymulti.end() && i->first == group); // until we change value
std::cout << "}\n"; // end something for the group
}
// end overal processing of the map
How to find duplicate files (same key and same value ) ?
Using the building blocks above, you could for every filename, you could create a temporary unsorted_map with the size as value, looking if the element is already in the temporary map (duplicate) or adding it (non duplicate).
If the whole purpose of your unsorted_multimapis to process these duplicates, then it would be pbetter, from the start to build an unosorted_map with filenames as keys, and value a multimap with size as sorted key and values, the other elements you collect on the file (full url ? inode ? wathever):
unsorted_map<string, multimap<long, filedata>> myspecialmap;

Finding the intersection of two vectors of strings

I have two vectors of strings and want to find the strings which are present in both, filling a third vector with the common elements. EDIT: I've added the complete code listing with the respective output so that things are clear.
std::cout << "size " << m_HLTMap->size() << std::endl;
/// Vector to store the wanted, present and found triggers
std::vector<std::string> wantedTriggers;
wantedTriggers.push_back("L2_xe25");
wantedTriggers.push_back("L2_vtxbeamspot_FSTracks_L2Star_A");
std::vector<std::string> allTriggers;
// Push all the trigger names to a vector
std::map<std::string, int>::iterator itr = m_HLTMap->begin();
std::map<std::string, int>::iterator itrLast = m_HLTMap->end();
for(;itr!=itrLast;++itr)
{
allTriggers.push_back((*itr).first);
}; // End itr
/// Sort the list of trigger names and find the intersection
/// Build a typdef to make things clearer
std::vector<std::string>::iterator wFirst = wantedTriggers.begin();
std::vector<std::string>::iterator wLast = wantedTriggers.end();
std::vector<std::string>::iterator aFirst = allTriggers.begin();
std::vector<std::string>::iterator aLast = allTriggers.end();
std::vector<std::string> foundTriggers;
for(;aFirst!=aLast;++aFirst)
{
std::cout << "Found:" << (*aFirst) << std::endl;
};
std::vector<std::string>::iterator it;
std::sort(wFirst, wLast);
std::sort(aFirst, aLast);
std::set_intersection(wFirst, wLast, aFirst, aLast, back_inserter(foundTriggers));
std::cout << "Found this many triggers: " << foundTriggers.size() << std::endl;
for(it=foundTriggers.begin();it!=foundTriggers.end();++it)
{
std::cout << "Found in both" << (*it) << std::endl;
}; // End for intersection
The output is then
Here is the partial output, there are over 1000 elements in the vector so I didn't include the full output:
Found:L2_te1400
Found:L2_te1600
Found:L2_te600
Found:L2_trk16_Central_Tau_IDCalib
Found:L2_trk16_Fwd_Tau_IDCalib
Found:L2_trk29_Central_Tau_IDCalib
Found:L2_trk29_Fwd_Tau_IDCalib
Found:L2_trk9_Central_Tau_IDCalib
Found:L2_trk9_Fwd_Tau_IDCalib
Found:L2_vtxbeamspot_FSTracks_L2Star_A
Found:L2_vtxbeamspot_FSTracks_L2Star_B
Found:L2_vtxbeamspot_activeTE_L2Star_A_peb
Found:L2_vtxbeamspot_activeTE_L2Star_B_peb
Found:L2_vtxbeamspot_allTE_L2Star_A_peb
Found:L2_vtxbeamspot_allTE_L2Star_B_peb
Found:L2_xe25
Found:L2_xe35
Found:L2_xe40
Found:L2_xe45
Found:L2_xe45T
Found:L2_xe55
Found:L2_xe55T
Found:L2_xe55_LArNoiseBurst
Found:L2_xe65
Found:L2_xe65_tight
Found:L2_xe75
Found:L2_xe90
Found:L2_xe90_tight
Found:L2_xe_NoCut_allL1
Found:L2_xs15
Found:L2_xs30
Found:L2_xs45
Found:L2_xs50
Found:L2_xs60
Found:L2_xs65
Found:L2_zerobias_NoAlg
Found:L2_zerobias_Overlay_NoAlg
Found this many triggers: 0
Possible Reason
I am starting to think that the way in which I compile my code is to blame. I am currently compiling with ROOT (the physics data analysis framework) instead of doing a standalone compile. I get the feeling that it doesn't work all that well with the STL Algorithm library and that's the cause of the issue, especially given how many people seem to have the code working for them. I will try to do a stand-alone compilation and re-running.
Passing foundTriggers.begin(), with foundTriggers empty, as the output argument will not cause the output to be pushed onto foundTriggers. Instead, it will increment the iterator past the end of the vector without resizing it, randomly corrupting memory.
You want to use an insert iterator:
std::set_intersection(wFirst, wLast, aFirst, aLast,
std::back_inserter(foundTriggers));
UPDATE: As pointed out in the comments, the vector is resized to be at least large enough for the result, so your code should work. Note that you should use the iterator returned from set_intersection to indicate the end of the intersection - your code ignores it, so you will also iterate over the empty strings left at the end of the output.
Could you post a complete test case so that we can see whether the intersection is actually empty or not?
Your allTrigers vector is empty, afterall. You never reset itr to the beginning of the map when you're filling it.
EDIT:
Actually, you never reset aFirst:
for(;aFirst!=aLast;++aFirst)
{
std::cout << "Found:" << (*aFirst) << std::endl;
};
// here aFirst == aLast
std::vector<std::string>::iterator it;
std::sort(wFirst, wLast);
std::sort(aFirst, aLast); // **** sorting empty range ****
std::set_intersection(wFirst, wLast, aFirst, aLast, back_inserter(foundTrigger));
// ^^^^^^^^^^^^^^
// ***** empty range *****
I hope you can now see why it is good practice to narrow down the scope of your variables.
You never use the return value of set_intersection. In this case you could use it to resize foundIterators after set_intersection has returned, or as the upper limit of the for loop. Otherwise your code seems to work. Can we see a full compilable program and its actual output please?

Erasing multiple objects from a std::vector?

Here is my issue, lets say I have a std::vector with ints in it.
let's say it has 50,90,40,90,80,60,80.
I know I need to remove the second, fifth and third elements. I don't necessarily always know the order of elements to remove, nor how many. The issue is by erasing an element, this changes the index of the other elements. Therefore, how could I erase these and compensate for the index change. (sorting then linearly erasing with an offset is not an option)
Thanks
I am offering several methods:
1. A fast method that does not retain the original order of the elements:
Assign the current last element of the vector to the element to erase, then erase the last element. This will avoid big moves and all indexes except the last will remain constant. If you start erasing from the back, all precomputed indexes will be correct.
void quickDelete( int idx )
{
vec[idx] = vec.back();
vec.pop_back();
}
I see this essentially is a hand-coded version of the erase-remove idiom pointed out by Klaim ...
2. A slower method that retains the original order of the elements:
Step 1: Mark all vector elements to be deleted, i.e. with a special value. This has O(|indexes to delete|).
Step 2: Erase all marked elements using v.erase( remove (v.begin(), v.end(), special_value), v.end() );. This has O(|vector v|).
The total run time is thus O(|vector v|), assuming the index list is shorter than the vector.
3. Another slower method that retains the original order of the elements:
Use a predicate and remove if as described in https://stackoverflow.com/a/3487742/280314 . To make this efficient and respecting the requirement of
not "sorting then linearly erasing with an offset", my idea is to implement the predicate using a hash table and adjust the indexes stored in the hash table as the deletion proceeds on returning true, as Klaim suggested.
Using a predicate and the algorithm remove_if you can achieve what you want : see http://www.cplusplus.com/reference/algorithm/remove_if/
Don't forget to erase the item (see remove-erase idiom).
Your predicate will simply hold the idx of each value to remove and decrease all indexes it keeps each time it returns true.
That said if you can afford just removing each object using the remove-erase idiom, just make your life simple by doing it.
Erase the items backwards. In other words erase the highest index first, then next highest etc. You won't invalidate any previous iterators or indexes so you can just use the obvious approach of multiple erase calls.
I would move the elements which you don't want to erase to a temporary vector and then replace the original vector with this.
While this answer by Peter G. in variant one (the swap-and-pop technique) is the fastest when you do not need to preserve the order, here is the unmentioned alternative which maintains the order.
With C++17 and C++20 the removal of multiple elements from a vector is possible with standard algorithms. The run time is O(N * Log(N)) due to std::stable_partition. There are no external helper arrays, no excessive copying, everything is done inplace. Code is a "one-liner":
template <class T>
inline void erase_selected(std::vector<T>& v, const std::vector<int>& selection)
{
v.resize(std::distance(
v.begin(),
std::stable_partition(v.begin(), v.end(),
[&selection, &v](const T& item) {
return !std::binary_search(
selection.begin(),
selection.end(),
static_cast<int>(static_cast<const T*>(&item) - &v[0]));
})));
}
The code above assumes that selection vector is sorted (if it is not the case, std::sort over it does the job, obviously).
To break this down, let us declare a number of temporaries:
// We need an explicit item index of an element
// to see if it should be in the output or not
int itemIndex = 0;
// The checker lambda returns `true` if the element is in `selection`
auto filter = [&itemIndex, &sorted_sel](const T& item) {
return !std::binary_search(
selection.begin(),
selection.end(),
itemIndex++);
};
This checker lambda is then fed to std::stable_partition algorithm which is guaranteed to call this lambda only once for each element in the original (unpermuted !) array v.
auto end_of_selected = std::stable_partition(
v.begin(),
v.end(),
filter);
The end_of_selected iterator points right after the last element which should remain in the output array, so we now can resize v down. To calculate the number of elements we use the std::distance to get size_t from two iterators.
v.resize(std::distance(v.begin(), end_of_selected));
This is different from the code at the top (it uses itemIndex to keep track of the array element). To get rid of the itemIndex, we capture the reference to source array v and use pointer arithmetic to calculate itemIndex internally.
Over the years (on this and other similar sites) multiple solutions have been proposed, but usually they employ multiple "raw loops" with conditions and some erase/insert/push_back calls. The idea behind stable_partition is explained beautifully in this talk by Sean Parent.
This link provides a similar solution (and it does not assume that selection is sorted - std::find_if instead of std::binary_search is used), but it also employs a helper (incremented) variable which disables the possibility to parallelize processing on larger arrays.
Starting from C++17, there is a new first argument to std::stable_partition (the ExecutionPolicy) which allows auto-parallelization of the algorithm, further reducing the run-time for big arrays. To make yourself believe this parallelization actually works, there is another talk by Hartmut Kaiser explaining the internals.
Would this work:
void DeleteAll(vector<int>& data, const vector<int>& deleteIndices)
{
vector<bool> markedElements(data.size(), false);
vector<int> tempBuffer;
tempBuffer.reserve(data.size()-deleteIndices.size());
for (vector<int>::const_iterator itDel = deleteIndices.begin(); itDel != deleteIndices.end(); itDel++)
markedElements[*itDel] = true;
for (size_t i=0; i<data.size(); i++)
{
if (!markedElements[i])
tempBuffer.push_back(data[i]);
}
data = tempBuffer;
}
It's an O(n) operation, no matter how many elements you delete. You could gain some efficiency by reordering the vector inline (but I think this way it's more readable).
This is non-trival because as you delete elements from the vector, the indexes change.
[0] hi
[1] you
[2] foo
>> delete [1]
[0] hi
[1] foo
If you keep a counter of times you delete an element and if you have a list of indexes you want to delete in sorted order then:
int counter = 0;
for (int k : IndexesToDelete) {
events.erase(events.begin()+ k + counter);
counter -= 1;
}
You can use this method, if the order of the remaining elements doesn't matter
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector< int> vec;
vec.push_back(1);
vec.push_back(-6);
vec.push_back(3);
vec.push_back(4);
vec.push_back(7);
vec.push_back(9);
vec.push_back(14);
vec.push_back(25);
cout << "The elements befor " << endl;
for(int i = 0; i < vec.size(); i++) cout << vec[i] <<endl;
vector< bool> toDeleted;
int YesOrNo = 0;
for(int i = 0; i<vec.size(); i++)
{
cout<<"You need to delete this element? "<<vec[i]<<", if yes enter 1 else enter 0"<<endl;
cin>>YesOrNo;
if(YesOrNo)
toDeleted.push_back(true);
else
toDeleted.push_back(false);
}
//Deleting, beginning from the last element to the first one
for(int i = toDeleted.size()-1; i>=0; i--)
{
if(toDeleted[i])
{
vec[i] = vec.back();
vec.pop_back();
}
}
cout << "The elements after" << endl;
for(int i = 0; i < vec.size(); i++) cout << vec[i] <<endl;
return 0;
}
Here's an elegant solution in case you want to preserve the indices, the idea is to replace the values you want to delete with a special value that is guaranteed not be used anywhere, and then at the very end, you perform the erase itself:
std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9};
// marking 3 elements to be deleted
vec[2] = std::numeric_limits<int>::lowest();
vec[5] = std::numeric_limits<int>::lowest();
vec[3] = std::numeric_limits<int>::lowest();
// erase
vec.erase(std::remove(vec.begin(), vec.end(), std::numeric_limits<int>::lowest()), vec.end());
// print values => 1 2 5 7 8 9
for (const auto& value : vec) std::cout << ' ' << value;
std::cout << std::endl;
It's very quick if you delete a lot of elements because the deletion itself is happening only once. Items can also be deleted in any order that way.
If you use a a struct instead of an int, then you can still mark an element of that struct, for ex dead=true and then use remove_if instead of remove =>
struct MyObj
{
int x;
bool dead = false;
};
std::vector<MyObj> objs = {{1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}};
objs[2].dead = true;
objs[5].dead = true;
objs[3].dead = true;
objs.erase(std::remove_if(objs.begin(), objs.end(), [](const MyObj& obj) { return obj.dead; }), objs.end());
// print values => 1 2 5 7 8 9
for (const auto& obj : objs) std::cout << ' ' << obj.x;
std::cout << std::endl;
This one is a bit slower, around 80% the speed of the remove.