Better way to iterate std::map - c++

Given a map, I need to retrieve and operate two immediately stored items.
To me, working on a vector is litter easier since I can do "iter + 1" or "iter - 1".
While for map, I am out of luck.
For example, I give a simple example as follows:
Note: in my real application, I don't simply subtract those numbers.
int main ()
{
map<char,int> mymap;
map<char,int>::iterator it;
mymap['b'] = 100;
mymap['a'] = 200;
mymap['c'] = 300;
// show content:
map<char,int>::iterator firstItem = mymap.begin();
map<char,int>::iterator secondItem = ++mymap.begin();
for ( ; secondItem != mymap.end(); ++firstItem, ++secondItem )
cout << secondItem->second - firstItem->second << endl;
return 0;
}
Question> Is there a better solution for this?
Thank you

Instead of incrementing both iterators in the loop control (incrementing is a bit slow), just assign firstItem = secondItem then increment secondItem.

You can do it with a single iterator. Move the increment from the header to the middle of your loop, and exit the loop when you hit the end of your map, like this:
map<char,int>::iterator item = mymap.begin();
for (;;) {
int first = item->second;
++item;
if ( item == mymap.end()) break;
cout << item->second - first << endl;
}

This is a matter of style. You can do eg.
auto first = m.begin();
if (first != m.end())
{
auto second = first;
second++;
for (; second != m.end(); first = second++)
{
...
}
}
You can also bailout more elegantly in the case where the map is empty. For instance you can do:
if (m.empty()) return;
auto first = m.begin(), second = first;
for (second++; second != m.end(); first = second++)
{
...
}
I'd favor the latter if I can, and use the former only if I must.

Your current loop will show undefined behaviour if the map is empty.
Your loop could be rewritten (more simply, and checking for an empty map) like so:
int main(int argc, char * argv[])
{
map<char,int> mymap;
map<char,int>::iterator it;
mymap['b'] = 100;
mymap['a'] = 200;
mymap['c'] = 300;
for ( it = ( mymap.begin() == mymap.end() ? mymap.end() : std::next(mymap.begin()) ) ; it != mymap.end(); ++it )
cout << it->second - std::prev(it)->second << endl;
return 0;
}

Your code will have undefined behavior if the map is empty but other than that it seems to be a reasonable approach, depending on your overall goal. Since map iterators are not random access you can't just add or subtract one, only increment/decrement.
An alternate approach is to make a copy of the iterator and then incrementing inside the loop.

Neither better, nor worse, just an alternative:
if (map.size() >=2)
std::accumulate(
++mymap.begin(),
mymap.end(),
mymap.begin(),
[](mymap_type::const_iterator iprev, mymap_type::value_type const& entry)->mymap_type::const_iterator
{
/* do something */;
return ++iprev;
});

Related

std::map iterate through keys with index of key

I need to iterate through the keys of a map, but looking ahead to future keys. For example:
map<int, int> m;
vector<int> v;
for(map<int,int>::iterator it = m.begin(); it != m.end(); ++it) {
cout << it->first << "\n";
//is the next element equal to 3?
auto next = it++;
std::cout << "equals 3" << next==3 << std::endl
}
but sometimes I don't want to see the next element (n+1), maybe I want to see the n+10 element, etc. How do I do this? If my list has 100 elements, and I arrive at element 99, then 99+10 is gonna break evrything. Is there a way to test if my iterator can achieve n+10?
The best solution I thougth of is to keep track of an index i and see if I can call it + 10 (that is, if i+10<mapSize). Bus is there a more elegant way? Maybe testing if the n+10 iterator exists or something?
Map does not sound like the appropiate data type for your use case. Try switching to a container that supports random access
I think that your are looking for something like std::advance (Please see here), but with an additional check, if the advance operation was past the end or not.
We can use a small lambda to do this kind of check. Since it uses only an increment operation, it should work for all type of containers.
Please see the following example to illustrate the function:
#include <iostream>
#include <map>
#include <iterator>
using Type = std::map<int, int>;
using TypeIter = Type::iterator;
int main() {
// Lambda to advance a container iterator and check, if that was possible
auto advanceAndCheck = [](const Type& t, const TypeIter& ti, size_t advance) -> std::pair<bool, TypeIter>
{ TypeIter i{ ti }; while ((i != t.end()) && (advance--)) ++i; return { i != t.end(), i }; };
// Test data
Type m{ {1,1}, {2,2}, {3,3}, {4,4}, {5,5} , {6,6} };
// Iterate over container
for (TypeIter it = m.begin(); it != m.end(); ++it) {
// Show some values
std::cout << it->first << "\n";
// Test
{
// Advance and check
auto [OK, itn] = advanceAndCheck(m, it, 1);
if (OK && itn->first == 3) std::cout << "The next Element is 3\n";
}
{
// Advance and check
auto [OK, itn] = advanceAndCheck(m, it, 5);
if (OK && itn->first == 6) std::cout << "The 5th next Element is 6\n";
}
}
}

Comparing 2 std::lists of std::pairs

I am hoping you can help me out here. I have searched for other answers, but I havent found something that matches my specific situation (but if you do find one, please let me know the URL!). I have seen a lot of suggestions about using std::map instead of list and I dont mind switching the container if need be.
Currently, I have two Lists of pairs i.e.
std:list <std::pair<string,string>> outputList1;
std:list <std::pair<string,string>> outputList2;
I have populated each list with User Settings that I have retrieved from an SQL database (I omit the SQL retrieval code here).
Example list:
outputList1 (first, second)
CanSeeAll, True
CanSubmit, False
CanControl, False
OutputList2:
CanSeeAll, False
CanSubmit, True
CanControl, False
I want to iterate through both lists and find the mismatches. For example, find the first string of the first pair of the first list to find the matching first string in the second list, then compare the second string to determine whether they match, then print out the non matching pairs to a new string (eventually to file), and so on.
In this example, the final string would have CanSeeAll and CanSubmit as the final output since those are the two that mismatch.
Here is what I've tried so far, but I get a blank string:
std::list <std::pair<std::string,std::string>>::iterator it1 = outputList1.begin();
std::list <std::pair<std::string,std::string>>::iterator it2 = outputList2.begin();
string token;
while (it1 != outputList1.end()){
if((*it1).first == ((*it2).first))
{
if((*it1).second != ((*it2).second))
{
token.append((*it1).first);
token.append(",");
token.append((*it1).second);
token.append("\r\n");
}
it1++;
it2 = outputList2.begin();
}
it2++;
if (it2 == outputList2.end())
it1++;
}
I know this logic is flawed as it will skip the first pair on the second list after the first iteration, but this is the best I can come up with at the moment, and I am banging my head on the keyboard a the moment.
Thanks everyone!
As I understand the problem,
you want to compare every element of one list, to every other element of another list.
You could use a pair of nested range based for loops.
#include <list>
#include <string>
int main(){
std::list<std::pair<std::string,std::string>> l1;
std::list<std::pair<std::string,std::string>> l2;
for (auto x: l1){
for (auto y: l2){
//compare x to y
}
}
}
The answer uses an auxiliary map but, have in mind you will get better result if you use two maps (or hash tables) instead of two list.
// create a map for elements in l2
std::map<std::string, std::string> l2map;
// move elements from l2 to the map so we get O(N*log(N)) instead of O(n²)
for (std::list<std::pair<std::string,std::string> >::iterator it = l2.begin();
it != l2.end();
++it)
{
l2map.insert(*it);
}
// walk l1 and look in l2map
for (std::list<std::pair<std::string,std::string> >::iterator l1it = l1.begin();
l1it != l1.end();
++l1it)
{
// look for the element with the same key in l2
// l1it->first is the key form l1
std::map<std::string, std::string>::iterator l2it = l2map.find(l1it->first);
if (l2it != l2map.end()) {
// found, then compare
if (l1it->second != l2it->second) { // l1it->second is the value from l1
// mismatch
}
} else {
// not in l2
}
}
You could use std::mismatch with the pre-condition: all settings occur in the same order in both lists (you could do a sort if this is not the case)
auto iterPair = std::mismatch(l1.begin(), l1.end(), l2.begin());
while (iterPair.first != l1.end()) {
// TODO: Handle the mismatching iterators
iterPair = std::mismatch(iterPair.first + 1, l1.end(), iterPair.second + 1);
}
If the keys in your lists come in the same order, as in your example, you can traverse the lists linearly:
std::ostringstream s;
std:list<std::pair<string, string>>::const_iterator i2(outputList2.cbegin());
for(auto const &pair: outputList1) {
if(pair.second != i2->second) {
s << pair.first << ": " << pair.second << " != " << i2->second << endl;
}
++i2;
}
Alternatively, use STL algorithms:
#include <algorithm>
typedef std::list<std::pair<std::string, std::string>> List;
std::ostringstream s;
for(
auto itrs(
std::mismatch(
outputList1.cbegin(), outputList1.cend(), outputList2.cbegin()
, [](auto const &l, auto const &r){ return l.second == r.second; }))
; itrs.first != outputList1.cend()
; itrs = std::mismatch(itrs.first, outputList1.cend(), itrs.second
, [](auto const &l, auto const &r){ return l.second == r.second; }))
{
s << itrs.first->first << ": "
<< itrs.first->second << " != " << itrs.second->second
<< std::endl;
}

What i am doing wrong in erasing some vector elements?

Here i am trying to print the frequency of each word in the sentence, which is stored in the vector of string
void display_by_word (vector<string> vs) //pass by value is necessary because we need to delete the elements.
{
vector<string> :: size_type vec_size, i;
string to_cmp = vs[0];
int occ = 0;
for ( i = 0; i < vs.size(); ++i){
vector <string> :: iterator it = vs.begin() + 1;
occ = 1;
for ( it ; it != vs.end(); ++it){
if ( vs[i] == *it){
vs.erase(it);
occ++;
}
}
cout << vs[i] << " " << occ << endl;
}
}
Sometimes it works fine but sometimes it crashes.what is wrong?
See http://en.cppreference.com/w/cpp/container/vector/erase
Invalidates iterators and references at or after the point of the erase [...]
After the erase has happened, you cannot reuse it because it has been invalidated. It's undefined behaviour, which can include random crashes.
However, erase returns an iterator to the element following the erased one, or to end() if it was the last element, which is why the solution with it = vs.erase(it); works.
Alternatively, consider using std::remove_if, followed by the two-argument erase, which is known as the Erase-Remove Idiom. It may turn out to be more elegant and more readable than a hand-written loop. Or just rewrite the whole function to use std::count_if.
You may want to rewrite the loop as something like this
while(it != vs.end())
{
if ( vs[i] == *it){
it = vs.erase(it);
occ++;
}
else
it++;
}
You may should do as below:
`
for ( it ; it != vs.end(); ){
if ( vs[i] == *it){
it = vs.erase(it);
occ++;
}
else{
++it;
}
}
`

How to iterate std::set?

I have this code:
std::set<unsigned long>::iterator it;
for (it = SERVER_IPS.begin(); it != SERVER_IPS.end(); ++it) {
u_long f = it; // error here
}
There is no ->first value.
How I can obtain the value?
You must dereference the iterator in order to retrieve the member of your set.
std::set<unsigned long>::iterator it;
for (it = SERVER_IPS.begin(); it != SERVER_IPS.end(); ++it) {
u_long f = *it; // Note the "*" here
}
If you have C++11 features, you can use a range-based for loop:
for(auto f : SERVER_IPS) {
// use f here
}
Another example for the C++11 standard:
set<int> data;
data.insert(4);
data.insert(5);
for (const int &number : data)
cout << number;
Just use the * before it:
set<unsigned long>::iterator it;
for (it = myset.begin(); it != myset.end(); ++it) {
cout << *it;
}
This dereferences it and allows you to access the element the iterator is currently on.
How do you iterate std::set?
int main(int argc,char *argv[])
{
std::set<int> mset;
mset.insert(1);
mset.insert(2);
mset.insert(3);
for ( auto it = mset.begin(); it != mset.end(); it++ )
std::cout << *it;
}
One more thing that might be useful for beginners is , since std::set is not allocated with contiguous memory chunks , if someone want to iterate till kth element normal way will not work.
example:
std::vector<int > vec{1,2,3,4,5};
int k=3;
for(auto itr=vec.begin();itr<vec.begin()+k;itr++) cout<<*itr<<" ";
std::unordered_set<int > s{1,2,3,4,5};
int k=3;
int index=0;
auto itr=s.begin();
while(true){
if(index==k) break;
cout<<*itr++<<" ";
index++;
}

How do you determine the last valid element in a STL-Container

If i iterate over a STL container i sometimes need to know if the current item is the last one in the sequence. Is there a better way, then doing something like this? Can i somehow convert rbegin()?
std::vector<int> myList;
// ....
std::vector<int>::iterator lastit = myList.end();
lastit--;
for(std::vector<int>::iterator it = myList.begin(); it != myList.end(); it++) {
if(it == lastit)
{
// Do something with last element
}
else
{
// Do something with all other elements
}
Try the following
std::vector<int>::iterator it2 = (++it);
if ( it2 == myList.end() ) {
...
}
The following should work as well
if ( it+1 == myList.end() ) {
// it is last
...
}
Maybe you can iterate backwards (use rbegin/rend) and put your special task before the loop or replace the end check with it != lastit and put the special handling after the loop
I would have some doubts about my design if some elements need to be treated differntly, but this suggestion is a bit cleaner for me (don't forget to test for empty containers)
std::vector<int>::iterator lastit = myList.end();
if (lastit != myList.begin())
{
lastit--;
for(std::vector<int>::iterator it = myList.begin(); it != lastit; ++it)
{
// Do
}
// Do with last
}
Use reversed iteration, this way you will have only one end()-1-like computation (notice the rbegin()+1) and no comparsions:
for(vector<int>::iterator it = myValues.rbegin()+1; it != myValues.rend(); it++) {
cout << *it << endl;
}
cout << "Process last one: " << *myValues.rbegin() << endl;
Also, for the vector<>, computing end()-1 is probably fast, so you can also do it like following:
for(vector<int>::iterator it = myValues.begin(); it != myValues.end()-1; it++) {
cout << *it << endl;
}
cout << "Process last one: " << *myValues.rbegin() << endl;
If you don't want to process the element after the loop, you can:
for(vector<int>::iterator it = myValues.rbegin(); it != myValues.rend(); it++) {
if(it == myValues.rbegin())
cout << "Process last one: " << *it << endl;
else
cout << *it << endl;
}
For a random access iterator like that for vector, you don't need the temporarary. You can say:
if ( it + 1 == v.end() ) {
// at one before end
}
Edit: And even for non-random access types one could use std:;distance:
if ( distance( it, v.end() ) == 1 ) {
// at one before end
}
An important question is: why create a loop if you do something special for 1 element. Why not do something special to the 3rd element? To every 4rth? ...
Just iterate over the elements to be treated the same, write separate code to treat the others.
Have a look at answers to this question, too.
Why not:
if(!myList.empty())
last_it = myList.begin() + myList.size()-1;
else
last_it = myList.end();
//or
last_it = myList.empty() ? myList.end() : myList.begin() + myList.size() - 1;
If you're using a vector, it's actually much simpler to use an integer index to iterate:
std::vector<int> myList;
for (unsigned int i = 0; i < myList.size(); i++)
{
if (i == (myList.size() - 1))
{
processDifferently (myList[i])
}
else
{
process (myList[i])
}
}
Minimizing the number of calls to myList.size() is left as an exercise for the OP :)