std::unique and vector::resize - c++

How does the unique and resize maintain that no repetition occurs:
for(map <string, vector<int> >::iterator it = m.begin(); it != m.end(); ++it)
{
vector<int>::iterator sz = unique((it->second).begin(),(it->second).end());
(it->second).resize(distance((it->second).begin(),sz));
}
The vector is sorted.

So basically the question is about reading documentation.
std::unique() pulls elements to the back of the vector, and returns an iterator to the first non unique element.
In the beginning sequence may look like: 1 1 2 2 2 3 6 6
After the call to unique the sequence changes to: 1 2 3 6 ...(duplicated).
Then distance() calculates how many unique elements are, which is distance from first unique element to first non unique element. .resize() just cuts off non unique part.
.erase(sz, (it->second).end); would be better to use in this case. Also the loop itself is rather weird.

Related

Remove last occurrence of an element in a vector STL

How can I remove only the last occurrence of a value in a C++ vector?
I have this pice of code.
if(vect.erase(std::remove(vect.begin(), vect.end(), oldVal),vect.end()) == vect.end()){
cont++;
}
vect.push_back(newVal);
It removes all instances of a value in the array. I need it to remove only the last specific element in the vector.
Example
Vector: 1 3 4 5 3 5 3 8 3 6
End I want to remove a '3' then should get:
1 3 4 5 3 5 3 8 6
Is there any canonical solution or should I try a stack os list?
std::find will find an element
std::reverse_iterator, accessed by myVector.rbegin() allows you to search from the back.
erase() as above.
Something like:
auto foundIt = std::find(vect.rbegin(), vect.rend(), oldVal);
// Find first from the back, i.e. last
if (foundIt != vect.rend()) { // if it was found
// get back to the right iterator
auto toRemove = --(foundIt.base());
// and erase it
vect.erase(toRemove);
}

C++ understanding iter and erase

I am just looking at a code, and I must say that I don't fully understand it yet.
vector<long>::iterator iter1;
vector<long>::iterator iter2;
while(m_vPitchMarks[0]<=vPitchPeriode[0])
{
iter1 = m_vPitchMarks.begin();
iter2 = vPitchPeriode.begin();
m_vPitchMarks.erase(iter1);
vPitchPeriode.erase(iter2);
if((m_vPitchMarks.size()==0)||(vPitchPeriode.size()==0))
break;
}
I am trying to break it down:
Here we will do a while-statement while the value of the first element of m_vPitchMarks is smaller than the value of the first element of vPitchPeriod.
while(m_vPitchMarks[0]<=vPitchPeriode[0])
{
}
Here we set something like a reference to the first element (element at index [0]) of both vectors.
iter1 = m_vPitchMarks.begin();
iter2 = vPitchPeriode.begin();
Now we erase all elements from m_vPitchMarks<> that have just this value.
For example, if iter1 had a value of 15, all elements in m_vPitchMarks<> that also have the value of 15 will be deleted, and the vector becomes shortened.
m_vPitchMarks.erase(iter1);
vPitchPeriode.erase(iter2);
Is that correct?
Thank you.
Almost as you say this:
while(m_vPitchMarks[0]<=vPitchPeriode[0])
will loop while the first value of m_vPitchMarks is less than or equal to the first value of vPitchPeriode.
Your explanation for this this code:
iter1 = m_vPitchMarks.begin();
iter2 = vPitchPeriode.begin();
m_vPitchMarks.erase(iter1);
vPitchPeriode.erase(iter2);
is not quite right. It simply removes the first value from both the vectors.
If you were using a std::deque or a std::list instead then you would just call pop_front to get the same result.
Then if we're out of values stop looping:
if((m_vPitchMarks.size()==0)||(vPitchPeriode.size()==0))
break;
Seems a particularly inefficient route given erasing an element from the front of a vector requires shifting every element that is left.

Vector of pairs to map

I have a little problem.
I have a vector of pairs patternOccurences. The pairs are <string,int>, where string is the pattern(name) and int the index where it appears. My problem is that patternOccurences has multiple pairs with the same .first(same pattern) but different int values.
For example: The vector has 10 entries. 5 of pattern "a" and 5 of pattern "b". all have different indices. Now i want to have a map (or something similar) so that i have a vector/list with each pattern(in my example "a" and "b") as a key and a vector of their indices as the value. The indices are in the different pairs in my vector of pairs and i want all indices for pattern "a" in a int vector as value for key "a".
I tried the following:
std::map<std::string,std::vector<int>> occ;
for(int i = 0;i<patternOccurences.size();i++){
if(occ.find(patternOccurences.at(i).first)==occ.end()){
occ[patternOccurences.at(i).first]=std::vector<int>(patternOccurences.at(i).second);
}
else{
occ[patternOccurences.at(i).first].push_back(patternOccurences.at(i).second);
}
}
patternOccurences is the vector of pairs and occ the desired map. First i check if there is already an entry for the string(pattern) and if not i create one with a vector as value. If there is already one I try to push_back the vector with the index. However it doesnt seem to be working right. For the first pattern i get a vector with 0 only as values and for the second there are only 3 indices which are right and the other ones are 0 as well.
I hope you can help me.
Kazoooie
You are calling the constructor for the vector in the wrong way:
std::vector<int>(patternOccurences.at(i).second);
This creates a vector with N default constructed elements, not a vector with one element with value N. You need:
std::vector<int>(1, patternOccurences.at(i).second);
That should fix the problem, but your code doesn't have to be that complicated. The following would work just fine:
for(int i = 0;i<patternOccurences.size();i++){
occ[patternOccurences.at(i).first].push_back(patternOccurences.at(i).second);
}
or with C++11, the even simpler:
for(auto& p:patternOccurences) {
occ[p.first].push_back(p.second);
}
What you are asking for already exists in STL and it's called std::multimap (and std::unordered_multimap).
Take a look here. Basically it's a map which allows more values to have the same key.
std::multimap<std::string, int> occ;
occ.insert(std::pair<std::string,int>("foo", 5));
occ.insert(std::pair<std::string,int>("foo", 10));
std::pair<std::multimap<std::string,int>::iterator, std::multimap<std::string,int>::iterator> group = occ.equal_range("foo");
std::multimap<std::string,int>::iterator it;
for (it = ret.first; it != ret.second; ++it) {
..
}
Change this statement
occ[patternOccurences.at(i).first]=std::vector<int>(patternOccurences.at(i).second);
to
occ[patternOccurences.at(i).first]=std::vector<int>(1, patternOccurences.at(i).second);

Assertion Error, using STL Vector

for(myIterator = numbers.begin();myIterator != numbers.end() ;myIterator++)
{
resultVect.push_back(*myIterator+2);
numbers.erase(myIterator+2);
}
numbers consist of a series of numbers (eg 1,2,3,4,5,6,7)
Then I would like to erase every 3rd number.
Something like,
1 2 3 4 5 6 ( First round -> 3 is out)
1 2 4 5 6 ( Second round -> 6 is out)
1 2 4 5 ( Third round -> 4 is out)
and so on.
I will store the number that goes out in another vector (resultVect).
Im getting Assertion error. Pls advise tq
When you use erase for a vector, it will relocate the elements after the erase position so the iterators after that will be invalidated.
Second when you say iterator + 2 and that could go beyond the range of the vector too.
Removing an element from the vector invalidates all iterators to that element and beyond (in the current standard, there is an open issue to change this).
The first question is how efficient you want the process to be, if you don't care (much) about performance you can do a simple loop:
for (int i = 3; i < input.size(); i+=3) {
result.push_back(input[i]);
}
for (int i = (input.size()+2)/3 - 1; i >= 0; --i) {
input.erase(input.begin()+i*3);
}
If performance is critical, you can take a look at the std::remove algorithm and use the same approach to avoid doing multiple copies of the elements while you run the algorithm. Basically you need a read and a write head into the original container and you only copy from the read to the write location if the condition is not met.
Simply put: you cannot modify a vector while iterating it. The iterator will become invalid and that will get you an assertion.
To properly do what you want, you might consider creating a copy of the vector with values to keep, and a vector with values to remove. Then replace the number vector by the one with the values to keep.

C++ Multimap manipulation

I have created a multimap as I have repeating keys. But I want do an efficient manipulation so that I can generate a new multimap with subsequent higher keys aligned. This is what I mean:
This is what I have:
key values
11 qwer
11 mfiri
21 iernr
21 ghfnfjf
43 dnvfrf
This is what I want to achive
key values
11 qwer,iernr
11 mfiri,iernr
21 iernr,dnvfrf
21 ghfnfjf,dnvfrf
43 dnvfrf
I have about 10 million entries so I am looking for something efficient.
In above value "qwer,iernr" is one string.
Here's a simple way to do it:
auto cur = map.begin();
auto next = map.upper_bound(cur->first);
for(; next != map.end(); next = map.upper_bound(cur->first))
{
for(; cur != next; ++cur)
{
cur->second += ", ";
cur->second += next->second;
}
}
... given a std::multimap<int, std::string> map;
However, any operation transforming 10m+ elements isn't going to be super fast.
Looks like straight-forward way would work fine. Map elements will be laid out in ascending order (assuming compare operator suits you). So just going through the equal ranges and modifying them with value of the element just after the range will do what you want.
Clone map (if you need the original), take first element, get equal_range() for its key, modify values with value of second iterator in the range (unless it is the last one). Get equal_range() for the key of second iterator. Repeat.
agree with Eugene ! also see following reference in terms of equal_range()
stl::multimap - how do i get groups of data?
To do this, you need to simply iterate through the map, while building the new map in order.
You can do this in two levels:
for (auto it=map.cbegin(); it != map.cend(); )
{
// The inner loop is over all entries having the same key
auto next_key_it=find_next_key_after(it);
for (; it != next_key_it; ++it) {
new_map.emplace_hint(new_map.end(), it->first, new_value(it->second, next_key_it));
}
}
The new_value function (or lambda) does the value transformation (or not, if the second parameter is map.end()).
The find_next_key_after(it) function returns the same as map.upper_bound(it->first), but could also be implemented as linear search for the first entry with different key.
It depends on your (expected) key distribution, which to use - if keys repeat a small, limited number of times, linear search is better; if the number of different keys is limited, with large equal key ranges, then upper_bound may be better.
For guaranteed complexity, linear search is better: The whole algorithm then has O(n) complexity. Which is as efficient as you can get.