I am trying to remove all duplicates in a vector of sorted strings. However, I keep on getting errors and I cannot really figure out why. I believe it has something to do with the vector resizing while also being used in a loop. Here is the code:
auto it = name.begin() + 1;
int count = 1;
while(it != name.end())
{
if (*it == *(it - 1))
{
count++;
it++;
}
else if (*it != *(it - 1) && count > 1) {
it = name.erase(it - count , it - 1);
cout << *it << " occurs " << count << " times." << endl;
count == 1;
}
}
The code checks to see if the previous element is duplicated and then removes it. It is important to know how many times the element occured as I would have to. Any idea how to fix this?
After you erased duplicated elements the current element does not have the preceding element.
else if (*it != *(it - 1) && count > 1) {
it = name.erase(it - count , it - 1);
cout << *it << " occurs " << count << " times." << endl;
count == 1;
}
Thus in the next iteration expressions *it == *(it - 1) and *it != *(it - 1) are invalid.
Moreover it can occur such a way that all elements before name.end() will be equal each other. In this case nothing will be erased.
You can use standard algorithm std::unique declared in header <algorithm>.For example
#include <algorithm>
#include <vector>
#include <string>
//..
name.erase( std::unique( name.begin(), name.end() ), name.end() );
Any idea how to fix this?
Fun effort. I believe I have identified 2 interesting possibilities.
Your loop started at the beginning of the vector 'name'. This is fine. Now you need to find a mechanism to delay the vector::erase() of these duplicate elements. "vector::erase()" invalidates any iterator at or after that erase. It may be why vectors support operations at the back ... we can take advantage of this.
Found on the internet: Iterator validity
Iterators, pointers and references pointing to
position (or first) and beyond are invalidated,
All iterators, pointers and references to elements before position
(or first) are guaranteed to keep referring to the same elements
they were referring to before the call.
So, by working first at the back of the names vector, we can minimize pointer invalidations.
What I did, and suggest you try, is to add a second vector to hold the duplicate-element-iterators. I labeled mine dups, with the following typedef's
typedef std::vector<std::string> Names;
typedef Names::iterator NamesIT;
// ---
typedef std::vector<NamesIT> NamesItVec;
vector<NamesItVec> dups.
When you have identified all the duplicates, and have push_back()'d the names-iterators into dups, you can then spin through dups, at that time erasing the duplicate name-elements.
Note that when you erase from the back of a vector, the erase() has an easier time of its re-arrangement in memory. (An effective 'second-use' of the earlier sorting effort.)
It looks like this:
std::cout << "\nremoving dups from vector name: back to front" << std::endl;
while (dups.size()) // as long as it has a iterator in it
{
auto dupIT = dups.back(); // reference closest element to strVec.end()
std::cout << "==> " << (*dupIT) << std::endl; // debug info
(void)names.erase(it); // erase the single element
dups.pop_back(); // remove the last element from dups
}
Hope this is not too much help. Good luck.
Related
I have this vector of names:
vector <string> names;
names.push_back("William");
names.push_back("Maria");
names.push_back("Petterson");
names.push_back("McCarthy");
names.push_back("Jose");
names.push_back("Pedro");
names.push_back("Hang");
I need to display this vector IN ORDER using a reverse iterator.
This is my attempt:
//Define a reverse iterator for the vector object
vector<string>::reverse_iterator itR = names.rend();
itR = itR - 1;
//Use the reverse iterator to display each element in the vector
cout << "\tNames:\n";
while (itR != names.rbegin())
{
cout << *itR << endl;
itR--;
}
This will display all names in correct order BUT it cuts off "Hang" at the end, any tips?
If you go from the end of a range to the beginning, you should check the equality first and then decrement inside the loop body. Otherwise there either is no iteration for the last element or the iterator gets decremented past the end resulting in undefined behaviour. You could use the following loop:
// print elements in original (= non-reversed) order
for (auto pos = names.rend(); pos != names.rbegin();)
{
--pos;
std::cout << *pos << std::endl;
}
The condition of the while statement
while (itR != names.rbegin())
prevents to output the element of the vector pointed to by the iterator names.rbegin().
Consider for example a vector that contains only one element. In this case after this statement
itR = itR - 1;
the iterator itR will be equal to names.rbegin() and the loop will be skipped.
Also it is unclear why you are starting to output the vector starting from the iterator names.rend() - 1 using the reverse iterator.
Either use original iterators or if you want to output the vector in the reverse order then write
//Use the reverse iterator to display each element in the vector
cout << "\tNames:\n";
for ( auto first = names.rbegin(), last = names.rend(); first != last; ++first )
{
cout << *first << endl;
}
Otherwise at least change your code like
//Define a reverse iterator for the vector object
vector<string>::reverse_iterator itR = names.rend();
//Use the reverse iterator to display each element in the vector
cout << "\tNames:\n";
while (itR != names.rbegin())
{
cout << *--itR << endl;
}
I have a list
list<pair<Zeitpunkt, double>> l_tempdiff;
And I only want to cout the first 5 elements.
I only know the way of couting the whole list with:
for (auto elem : l_tempdiff)
{
cout << elem.first << elem.second << endl;
}
I dont know how to acces my elements when I use:
for (it = l_tempdiff.begin(); it != l_tempdiff.end(); ++it)
{
}
And I guess I need to change the l_tempdiff.end() to some other value but it doesnt seem to take just the number5`. How can I do this?
Since std::list iterators are not random access you cannot just increment them like l_tempdiff.begin() + 5. What you can do is use std::next to increment the iterator the required number of times. That would looks like
for (auto it = l_tempdiff.begin(), end = std::next(l_tempdiff.begin(), 5); it != end; ++it)
{
// use `*it` here
}
Before doing this though you should make sure the list is big enough because if it isn't then you'll have undefined behavior.
You only want to output the first five elements?
Well, a for-range-loop is a good place to start, just add the additional constraint as a break-condition:
int i = 0;
for (auto&& elem : l_tempdiff)
{
if (5 < ++i) break;
cout << elem.first << elem.second << endl;
}
I change auto to auto&& to avoid needless copying.
As an aside, consider reading "Why is "using namespace std" considered bad practice?" and "C++: "std::endl" vs "\n"".
list<pair<Zeitpunkt,double> > :: iterator it;
int m = 0;
it = l_tempdiff.begin();
while( it != l_tempdiff.end() && m < 5)
{
cout<<it->second<<"\n";
m++;
it++;
}
Try
auto it = l_tempdiff.begin();
auto end = l_tempdiff.end();
for (int count = 0; count < 5 && it != end; ++count)
{
std::cout << it->first << it->second << std::endl;
std::advance(it);
}
This prints the first five pairs (or all the pairs, if there are less than 5).
count is used to control the maximum number of elements to be printed.
it is an iterator that, in each iteration of the loop, references the current pair.
Note that advancing an end iterator gives undefined behaviour. So it is necessary to terminate the loop if the end iterator is reached (hence the it != end test in the loop condition) or if the maximum number of elements (5) is reached.
What I'm confused about is that I have a map which is made up of size_t of string as the key, and strings as the value.
std::multimap<size_t, std::string> wordMap;
Then I have a pair that stores the equal_range for all strings with size of 4. Then I want to iterate through the start of that equal_range to the end of that equal_range. The start is my pair.first and end is my pair.second. How would I iterate through every character that my pair.first points too and then compare that to every word in between pair.first and pair.second ?
pair<multimap<size_t, string>::iterator, multimap<size_t, string>::iterator> key_range;
key_range = wordMap.equal_range(n);
Basically I want to compare every letter in word1 to every character in word2.
Advance itr2 which is word2 to the next word and compare every letter in that to every letter in word1. Do this for every word then advance itr1 which is word1 to another word and compare that to every word.
How would I get every character itr2 points to? I think the first for loop accomplishes this for the first iterator but I don't know how to do it for itr2.
for (word_map::iterator itr = key_range.first; itr != key_range.second; itr++) { //this loop will iterate through every word to be compared
for (word_map::iterator itr2 = next(key_range.first); itr2 != key_range.second; itr2++) { //this loop will iterate through every word being compared against itr1
int i = 0;
int hit = 0;
for (char& c1 : itr->first) {
char& c2{ (itr2)->first[i] };
if(c1 != c2)
hit++;
i++;
}
}
I'd like to compare every letter in every word against each other as long as they have the same string size. Then if hit == 1 that means the words are only off by 1 character and they should be mapped or stored in some type of STL container that groups them. I'm still new to STL so i'm thinking a set but I need to read more into it.
First, you'd be more likely to get assistance if you provided a minimal compilable example. I'm assuming your words are std::strings for this answer, but you know what they say about assuming.
There are algorithms like "zip" which is implemented in Boost specifically for iterating over mulitple collections simultaneously, but I don't think there's anything comparable in the standard library.
A simple but unpleasantly fiddly approach would be just to manually iterate through both strings. This will output each letter in the two words until either one word ends, or there's a difference.
Note all the fiddly bits: you need to make sure both iterators are valid at all times in case one word ends before the other, and working out what actually happened is a bit cumbersome.
#include <string>
#include <iostream>
int main()
{
std::string word1 = "capsicum";
std::string word2 = "capsicube";
std::string::iterator it1 = word1.begin();
std::string::iterator it2 = word2.begin();
while (it1 != word1.end() && it2 != word2.end())
{
// characters are different!
if (*it1 != *it2)
break;
// characters are the same
std::cout << "Both have: " << *it1 << std::endl;
// advance both iterators
++it1;
++it2;
}
if (it1 == word1.end() && it2 == word2.end())
{
std::cout << "Words were the same!" << std::endl;
}
else if (it1 == word1.end())
{
std::cout << "Word 1 was shorter than word 2." << std::endl;
}
else if (it2 == word2.end())
{
std::cout << "Word 1 was longer than word 2." << std::endl;
}
else
{
std::cout << "Words were different after position " << (it1 - word1.begin())
<< ": '" << *it1 << "' vs '" << *it2 << "'" << std::endl;
}
}
New answer, since the question was significantly updated. I'm still not sure this will do exactly what you want, but I think you should be able to use it to get where you want to go.
I've written this as a minimal, complete, verifiable example, which is ideally how you should pose your questions. I've also used C++11 features for brevity/readability.
Hopefully the inline comments will explain things sufficiently for you to at least be able to do your own research for anything you don't fully understand, but feel free to comment if you have any more questions. The basic idea is to store the first word (using the key_range.first iterator), and then start iterating from the following iterator using std::next(), until we reach the end iterator in key_pair.second.
This then gives us word1 outside of the loop, and word2 within the loop which will be set to every other word in the list. We then use the "dual interation" technique I posted in my other answer to compare each word character by character.
#include <map>
#include <string>
#include <iostream>
int
main()
{
std::multimap<size_t, std::string> wordMap;
wordMap.insert({4, "dogs"});
wordMap.insert({4, "digs"});
wordMap.insert({4, "does"});
wordMap.insert({4, "dogs"});
wordMap.insert({4, "dibs"});
// original type declaration...
// std::pair<std::multimap<size_t, std::string>::iterator, std::multimap<size_t, std::string>::iterator> key_range;
// C++11 type inference...
auto key_range = wordMap.equal_range(4);
// make sure the range wasn't empty
if (key_range.first == key_range.second)
{
std::cerr << "No words in desired range." << std::endl;
return 1;
}
// get a reference to the first word
std::string const& word1 = key_range.first->second;
std::cout << "Comparing '" << word1 << "' to..." << std::endl;
// loop through every iterator from the key_range, skipping for the first
// (since that's the word we're comparing everything else to)
for (auto itr = std::next(key_range.first); itr != key_range.second; ++itr)
{
// create a reference for clarity
std::string const& word2 = itr->second;
std::cout << "... '" << word2 << "'";
// hit counter; where hit is defined as characters not matching
int hit = 0;
// get iterators to the start of each word
auto witr1 = word1.begin();
auto witr2 = word2.begin();
// loop until we reach the end of either iterator. If we're completely
// confident the two words are the same length, we could only check
// one of them; but defensive coding is a good idea.
while (witr1 != word1.end() && witr2 != word2.end())
{
// dereferencing the iterators will yield a char; so compare them
if (*witr1 != *witr2)
++hit;
// advance both iterators
++witr1;
++witr2;
}
// do something depending on the number of hits
if (hit <= 1)
{
std::cout << " ... close enough!" << std::endl;
}
else
{
std::cout << " ... not a match, " << hit << " hits." << std::endl;
}
}
}
I'm currently trying to display the total no. of topicids and testids based on the name.
However I'm having trouble doing that display. I initially had a vector containing all the data.
For e.g.
user1:name:topic1:test1
user1:name:topic2:test1
user2:name:topic1:test1
user2:name:topic2:test1
Due to the multiple duplicates in the vector, I want to display in the following format:
username:name:numofTopics:numofTests
user1:name:2:2
user1:name:2:2
Therefore, i thought of comparing the name against the next name in the vector and push in the element to a new vector called singleAcc. The purpose of this is to display the duplicate element as ONE element.
Below is my code for displaying the data
vector<AccDetails> singleAcc;
for (vector<AccDetails>::iterator itr=accInfo.begin();itr!=accInfo.end()-1; ++itr) {
if (itr->name == itr[1].name) {
//cout << (*itr) << endl;
singleAcc.push_back(*itr);
}
}
for (vector<AccDetails>::iterator itr = singleAcc();itr!=singleAcc();++itr) {
cout << left
<< setfill(' ')
<< setw(20) << itr[0].username
<< setw(20) << itr[0].name
<< setw(20) << countTopics(itr->name)
<< setw(20) << countTests()
<< endl;
}
Problem:
On the first vector iteration, the name will not compare against the last element bcoz of accDetails.end()-1.
How to display the duplicate elements as ONE element? Is what I'm doing in the 2nd iteration the right thing?
Hope someone can help me with this. Or is there a better way to doing this?
Thanks!
Why this won't work
Your proposed solution simply won't work as intended. Consider three elements that are considered duplicates in consecutive subsequence (I am using numbers to simplify the concept):
[1,1,1]
The iterator will first compare 1 to 1, and then push_back the first one.
Then it will compare second 1 to the third one, which again returns true, and the result that was supposed to have no duplicates ends up:
[1,1]
So it's clearly not something you want to do. In general, it looks like a rather weird problem, but so solve this one part you've posted here, I suggest using std::multiset.
A better solution
Create a comparator that tests for the name field just like you do here.
Then, recovering unique fields is rather simple:
std::multiset<AccDetail> s;
for (auto element_it = s.begin(); element_it != s.end(); element_it = s.upper_bound(*element_it)) {
auto er = s.equal_range(*element_it);
// use this to get all the elements with given name
for (auto i = er.first; i != er.second; ++i)
cout << *i << " ";
// use this to get the number of them
cout << std::distance(er.first, er.second);
}
See a working sample on Coliru.
Bonus
You are right that the iterator in first loop would go over the bounds. The solution for that is rather simple: zip iterator which can handle this automatically.
for (auto elem_pair : zip(v, v | drop(1)))
if (elem_pair.first == elem_pair.second)
...
Boost.Range has tools that allow that code to work. It would still suffer from the problems I've mentioned, though.
For example map1 is gaving values 1 to 10 with some address(begin to end).
i want to have values 10 to 1 with corresponding address in map2(begin to end)
map<long , int* > v;
map<long , int* > rv;
int i,a[10];
for(i=0; i<10; i++)
{
a[i] = i+1;
v.insert(pair<long, int *>(i+1,&a[i]));
}
itr = v.begin();
while(itr != v.end())
{
cout << itr->first << " "<<itr->second;
cout << endl;
itr++;
}
rv.insert(v.rbegin(),v.rend());
cout << "copied array: "<<endl;
itr = rv.begin();
while(itr != rv.end())
{
cout << itr->first << " "<<itr->second;
cout << endl;
itr++;
}
i tried above one but am getting values 1 to 10 only..my expected values 10 to 1.
please help me to find out....
STL map is an ordered container. The order of items that you get during the iteration is independent of the order in which you insert the items into the container.
The order of iteration is determined by two things:
The value of the key, and
The Compare class passed as the template parameter to the map
You can iterate the map in reverse order (your code snippet shows that you already know how it is done). The performance penalty for reverse-iterating a map, if any, is negligible. You can also provide a non-default Compare (std::greater<long> instead of the default std::less<long>) to have the default order of iteration altered.
This is impossible because std::map is ordered associative container. If you want to preserve order of insertion use other containers such as std::list or std::vector.
Maps sort by increasing value (as dictated by operator< ), so no matter how you insert the elements, they will be returned in sorted order. You are certainly doing the insertion in reverse, but every element placed is being duly sorted into proper ascending order.