Map C++ Reverse Iterator - c++

map<int, string>::reverse_iterator& it = temp.rbegin();
it -> points to garbage key value
it++ -> points to the correct key value
map<int, string>::iterator& it = temp.begin();
it-> points to the correct key value from beginning.
Please assist.

Your statements are incorrect. If temp is not empty, then *temp.rbegin() is indeed the last value in the map, and *temp.begin() is the first value.
(However, the underlying iterator of the reverse begin is the ordinary end iterator - but you don't see that unless you call base() on the reverse iterator.)

You must have an error in your code that's filling the map. You can verify this by testing a trivial example such as
#include <algorithm>
#include <map>
#include <iostream>
using namespace std;
int main()
{
map<int, char> coll;
// insert elements from 1 to 9
for (int i=1; i<=9; ++i) {
coll[i] = static_cast<char>(i+'a'-1); // because adding 96 is less obvious that we're indexing based at letter a
}
// print all element in reverse order
for_each (coll.rbegin(), coll.rend(),
[]( pair<int, char> mapinfo ) { cout << mapinfo.second << " "; } );
cout << endl;
}

Related

Cannot increment value-initialized map/set iterator

I'm trying to increment key of nodes after the index j in a map, but I got some error while incrementing the iterator to the next node, here's my code :
typedef map<int, string> My_map;
My_map my_map;
my_map[0] = "la base";
my_map[1] = "le premier";
int j = 1;
My_map::reverse_iterator first_it(my_map.rbegin());
first_it++;
My_map::reverse_iterator last_it(make_reverse_iterator(next(my_map.begin(), j - 1)));
for (My_map::reverse_iterator it = first_it ; it != last_it ; it++) {
auto handle = my_map.extract(it.base());
handle.key()++;
my_map.insert(move(handle));
}
I know that a map cannot have duplicate keys, so I started incrementing from the last one to the j-th one. But the it++ does not work. Is it different from when I used first_it++; ?
In the documentation for std::map::extract it mentions the side-effects:
Extracting a node invalidates only the iterators to the extracted element. Pointers and references to the extracted element remain valid, but cannot be used while element is owned by a node handle: they become usable if the element is inserted into a container.
As you can see, all the iterators are find except for the ones you care about. Because it is an iterator to the extracted element, it is now invalid. Subsequent attempts to use it (with it++ to advance the loop iteration) leads to undefined behavior.
What you can do is use the iterator returned by std::map::insert:
auto result = my_map.insert(move(handle));
it = make_reverse_iterator(result.position);
As pointed by #paddy, after calling .extract() method and doing .insert() again all your iterators are invalidated hence you can't run modifying loop any further.
I suggest following solution which is valid and faster. Into separate std::vector copy all elements starting from j-th position. Delete them in map. Modify them in vector the way you like, for example by adding +1 to all keys. Insert them all back at once by .insert(begin, end) method of a map. Clear vector of copied elements.
All suggested is implemented in below code snippet:
Try it online!
#include <map>
#include <string>
#include <vector>
#include <iostream>
int main() {
std::map<int, std::string> m;
m[0] = "la base";
m[1] = "le premier";
int j = 1;
auto const j_it = std::next(m.begin(), j);
std::vector<std::pair<int, std::string>> vcopy(j_it, m.end());
m.erase(j_it, m.end());
for (auto & [k, v]: vcopy)
++k;
m.insert(vcopy.begin(), vcopy.end());
vcopy.clear();
// Show map
for (auto const & [k, v]: m)
std::cout << k << ": " << v << std::endl;
}
Output:
0: la base
2: le premier

How can I order a map that has duplicated values in C++?

I am trying to reorder the map in a descending way depending on the values, I have been trying to create a new map and insert the one which has the biggest value first but it keeps ordering the map by the keys.
I have also tried to reorder it by the value changing the form of the map into the other way but I will loose some data because I have more than one key which has the same value.
#include <iostream>
#include "SymbolFreq.h"
#include <string>
#include <fstream>
#include <streambuf>
#include <map>
using namespace std;
int main()
{
map <char, int> mymap;
map <char, int> realmap;
ifstream infile{ "ToCompress.txt" };
std::string str((std::istreambuf_iterator<char>(infile)),
std::istreambuf_iterator<char>());
std::map<char, int>::iterator itera;
for (auto it = str.begin(); it != str.end(); ++it)
{
itera = mymap.find(*it);
if (itera != mymap.end())
{
itera->second++;
}
else
{
mymap.insert({ *it, 1 });
}
}
int max = 0;
char provisionalChar;
int provisionalInt;
while (mymap.empty() == false)
{
for (auto it = mymap.cbegin(); it != mymap.cend(); ++it)
{
if (it->second > max)
{
max = it->second;
provisionalChar = it->first;
provisionalInt = it->second;
}
//cout << it->first << "\t" << it->second << "\n";
}
mymap.erase(provisionalChar);
realmap.insert({ provisionalChar, provisionalInt });
max = 0;
}
for (auto it = realmap.cbegin(); it != realmap.cend(); ++it)
{
cout << it->first << "\t" << it->second << "\n";
}
return 0;
}
If I understand the question properly, you'd like to count how many times each char appears in the file and then produce a map sorted with the char that appeared most time first.
Here's one idea:
#include <algorithm>
#include <cstdint>
#include <fstream>
#include <functional>
#include <iostream>
#include <iterator>
#include <map>
#include <string>
#include <unordered_map>
int main() {
std::ifstream infile{"ToCompress.txt"};
// "mymap" is only used for counting how many times each char appears.
std::unordered_map<char, std::uintmax_t> mymap;
// Loop directly over the file. No vector needed:
std::for_each(std::istreambuf_iterator<char>(infile),
std::istreambuf_iterator<char>(), [&mymap](char ch) {
// No need to find first - operator[] inserts an element
// for the key ("ch") if it's missing.
++mymap[ch];
});
// Transform the unordered_map into a multimap where the count is the key
// and in which we use a descending sort order (std::greater):
std::multimap<std::uintmax_t, char, std::greater<std::uintmax_t>> realmap;
std::transform(mymap.begin(), mymap.end(),
std::inserter(realmap, realmap.end()),
[](const auto& ch_co) -> std::pair<std::uintmax_t, char> {
// return a pair with key and value swapped
return {ch_co.second, ch_co.first};
});
// Print the result
for(auto& [count, ch] : realmap) {
std::cout << count << '\t' << ch << '\n';
}
}
Possible output:
537479
120204 t
113285 e
80681
80670 i
79862 n
77984 r
77464 s
69994 o
67377 a
...
Apparently, <space>, t, e and \n are tne most common characters in my C++ programs (which is what I used as input)
Your question may be ill-posed; take a step back and state what you are really trying to accomplish.
That said, I'll attempt an answer based on what you've written.
It looks like you're trying to sort an std::map by value, in which case your question is a duplicate of either this or this question.
Regarding your initial attempt:
Take a look at this table. Only sequence containers allow you to directly influence order. As with priority queue you have limited control over the order of associative containers and almost zero control for unordered containers.

Iterating through an unordered multimap

I have inserted some elements in my unordered_multimap and I am finding all the values mapped to a key k using equal range. Now I want to traverse these mapped values by their order of insertion. Look at the code for better understanding.
#include <iostream>
#include <unordered_map>
using namespace std;
int main()
{
unordered_multimap<int,int> m;
m.insert(make_pair(1,2));
m.insert(make_pair(1,3));
m.insert(make_pair(1,4));
auto it = m.equal_range(1);
for(auto it1 = it.first; it1 != it.second; it1++) {
cout<<it1->second<<endl;
}
}
output:
4
3
2
But I want to traverse in the order in which keys and mapped values were inserted. So, I want to traverse in order 2,3,4. Is it possible?
There is not a straghtforward way to do what you are asking for. When elements are inserted in an ordered or an unordered multimap they are actually placed in the internal structure and it is not known in which order they have been placed.
You should have an auxiliary e.g. an std::queue container for this where you append the iterator to the inserted element. The iterator can be obtained from the insertion as:
auto inserted_pos = m.insert(make_pair(1,4));
Keep in mind that iterators are not invalidated during insertion. They are invalidated if the element is removed, and only for the concerned element.
here's one way to achieve what you want.
It uses a few techniques made available by boost::multi_index.
Note the use of project to convert iterators in one index to iterators in another.
#include <iostream>
#include <vector>
#include <algorithm>
#include <utility>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
namespace example {
struct by_id {};
struct by_insertion_order {};
using namespace boost;
using namespace boost::multi_index;
using item_type = std::pair<int, int>;
typedef multi_index_container<
item_type, // what we are storing
indexed_by<
// unordered multimap-type index
hashed_non_unique<tag<by_id>, member<item_type, int, &item_type::first> >,
// sequence-type index - records insertion order
sequenced<tag<by_insertion_order>>
>
> my_store;
using const_insertion_sequence_iterator = decltype(std::declval<my_store>().get<by_insertion_order>().cbegin());
using const_by_id_iterator = decltype(std::declval<my_store>().get<by_id>().cbegin());
// convert a range of 'by_id' iterators to an ordered vector 'by_insertion_sequence' iterators
// #param store is a reference to the store for which the iterators are valid
// #param first is the first by_id iterator in the filtered range
// #param last is the 'one past the end' iterator of the filtered range
// #returns a vector of iterators to items ordered by insertion sequence
auto
projected_to_insertion_order(const my_store& store,
const_by_id_iterator first,
const_by_id_iterator last)
-> std::vector<const_insertion_sequence_iterator>
{
std::vector<const_insertion_sequence_iterator> result;
for ( ; first != last ; ++first) {
result.push_back(store.project<by_insertion_order>(first));
}
sort(result.begin(),
result.end(),
[&store](const auto& il, const auto& ir) {
return distance(store.get<by_insertion_order>().cbegin(), il)
< distance(store.get<by_insertion_order>().cbegin(), ir);
});
return result;
}
}
int main()
{
using namespace std;
using example::my_store;
using example::by_id;
using example::by_insertion_order;
using example::projected_to_insertion_order;
// define store
my_store m;
// add some items
m.get<by_id>().emplace(1,2);
m.get<by_id>().emplace(3,6);
m.get<by_id>().emplace(1,3);
m.get<by_id>().emplace(2,5);
m.get<by_id>().emplace(1,4);
// get range of items filtered by id
auto ip = m.get<by_id>().equal_range(1);
cout << "filtered but unordered\n";
for (auto it = ip.first ; it != ip.second ; ++it) {
cout << it->first << ":" << it->second << endl;
}
// project that to a vector of iterators to items ordered by insertion sequence
cout << "filtered and ordered by insertion sequence\n";
for (const auto& it : projected_to_insertion_order(m, ip.first, ip.second)) {
cout << it->first << ":" << it->second << endl;
}
}
expected output:
filtered but unordered
1:4
1:3
1:2
filtered and ordered by insertion sequence
1:2
1:3
1:4

How to use vector and struct?

I need to count letters from the string, sort them by count and cout results. For this purpose I'm trying to use vector and struct. Here is part of my code, but it's not working, because I don't know how to implement something:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
struct int_pair{
int key;
int value;
};
bool sort_by_value(int_pair left, int_pair right){
return left.value < right.value;
}
int main() {
string characters = "aasa asdfs dfh f ukjyhkh k wse f sdf sdfsdf";
vector<int_pair> most_frequent;
for (string::size_type i = 0; i <= characters.length(); i++) {
int int_char = (int)characters[i];
most_frequent[int_char]++; <-- I want to do something like this, but it's not working
}
sort(most_frequent.begin(), most_frequent.end(), sort_by_value);
for (vector<int_pair>::iterator it = most_frequent.begin(); it != most_frequent.end(); ++it) <-- is this call correct?
cout << " " << it->key << ":" << it->value << endl;
return 0;
}
At this code I have 2 parts that I don't know how to deal:
most_frequent[int_char]++; <-- I want to do something like this, but it's not working
and
for (vector<int_pair>::iterator it = most_frequent.begin(); it != most_frequent.end(); ++it) <-- is this call correct?
Maybe you can see any other mistakes and potential issues at this code.
I would use a std::map to determine the frequency of each letter, then copy that into a multimap while reversing the key and value to get them in order.
#include <iostream>
#include <map>
#include <algorithm>
template<class T, class U>
std::pair<U,T> flip_pair(const std::pair<T,U>& p) {
return std::make_pair(p.second,p.first);
}
int main(){
std::string characters = "zxcvopqiuweriuzxchajksdui";
std::map<char,int> freq;
std::multimap<int,char> rev_freq;
// Calculate the frequency of each letter.
for(char c: characters){
freq[c]++;
}
// Copy the results into a multimap with the key and value flipped
std::transform(std::begin(freq), std::end(freq),
std::inserter(rev_freq, rev_freq.begin()),
flip_pair<char,int>);
// Print out the results in order.
for(std::pair<int,char> p : rev_freq){
std::cout << p.first << ": " << p.second << std::endl;
}
};
This should do what you need:
most_frequent[int_char].key = int_char;
most_frequent[int_char].value++;
Yes, it sets the key many times, even though it doesn't need to.
When accessing the container with the key (vector is indexed with an integer, which is "the key" in your case), you don't have to store the key in the value field of the container again.
So you don't need your struct since you only need the value field and can can store the number of occurrences directly in the vector.
The idea is to fill the vector with 256 integers in the beginning, all initialized to zero. Then, use the vector index as your "key" (character code) to access the elements (number of occurrences).
This will result in a code similar to this:
// initialize with 256 entries, one for each character:
vector<int> counts(256);
for (string::size_type i = 0; i <= characters.length(); i++) {
// for each occurrence of a character, increase the value in the vector:
int int_char = (int)characters[i];
counts[int_char]++;
}
Once filling of the vector is done, you can find the maximum value (not only the value but also the key where it is stored) using the std::max_element algorithm:
vector<int>::iterator most_frequent =
std::max_element(counts.begin(), counts.end());
// getting the character (index within the container, "key"):
std::cout << (char)(most_frequent - counts.begin());
// the number of occurrences ("value"):
std::cout << (*most_frequent);
Here is your example with the changes (only printing the most frequent character, here it is the space so you don't see it): http://ideone.com/94GfZz
You can sort this vector, however, you will loose the key of course, since the elements will move and change their indices. There is a nice trick to process statistics like that: Use a reversed (multi)map (key, value reversed):
multimap<int,int> keyForOccurrence;
for (vector<int>::iterator i = counts.begin(); i != counts.end(); ++i) {
int occurrences = *i;
int character = i - counts.begin();
keyForOccurrence.insert(std::pair<int,int>(occurrences, character));
}
Updated code: http://ideone.com/Ub5rnL
The last thing you should now sort out by yourself is how to access and process the data within this map. The fancy thing about this reversed map is that it is now automatically sorted by occurrence, since maps are sorted by key.
I find more natural to use a std::map container to store each character occurrences. The character is map's key, its occurrence count is map's value.
It's easy to scan the source string and build this map using std::map::operator[], and ++ to increase the occurrence count.
Then, you can build a second map from the above map, with key and value inverted: so this map will be sorted by occurrences, and then you can print this second map.
Note that you have to use a std::multimap as this second map, since its keys (i.e. the occurrences) can be repeated.
Sample code follows (I tested it with VS2010 SP1/VC10):
#include <stddef.h> // for size_t
#include <algorithm> // for std::transform
#include <functional> // for std::greater
#include <iostream> // for std::cout
#include <iterator> // for std::inserter
#include <map> // for std::map, std::multimap
#include <ostream> // for std::endl
#include <string> // for std::string
#include <utility> // for std::pair
using namespace std;
int main()
{
string str = "aasa asdfs dfh f ukjyhkh k wse f sdf sdfsdf";
// Build the occurrences map (char -> occurrences)
map<char, size_t> freq;
for (size_t i = 0; i < str.length(); ++i)
freq[ str[i] ]++;
// Build a new map from previous map with inverted <key, value> pairs,
// so this new map will be sorted by old map's value (i.e. char's
// occurrences), which is new map's key.
// Use the std::greater comparator to sort in descending order.
multimap<size_t, char, greater<size_t>> sorted_freq;
transform(
freq.begin(), freq.end(), // source
inserter(sorted_freq, sorted_freq.begin()), // destination
[](const pair<char, size_t>& p) // invert key<->value
{
return pair<size_t, char>(p.second, p.first);
}
);
// Print results
for (auto it = sorted_freq.begin(); it != sorted_freq.end(); ++it)
cout << it->second << ": " << it->first << endl;
}
Output:
: 9
s: 7
f: 7
d: 5
a: 4
k: 3
h: 3
u: 1
w: 1
y: 1
j: 1
e: 1
If you don't want to print the space character occurrences, you can easily filter that out.
Note that using std::map/std::multimap will also scale up better than std::vector for non-ASCII characters, e.g. if you use Unicode UTF-32 (since Unicode characters are much more than just 256).

Why can I not convert a reverse iterator to a forward iterator?

Well, I know why, it's because there isn't a conversion, but why isn't there a conversion? Why can forward iterators be turned to reverse iterators but not the other way round? And more importantly, what can I do if I want to do this? Is there some adapter that allows you to iterate backwards using a forward iterator?
std::vector<int> buffer(10);
std::vector<int>::iterator forward = buffer.begin();
std::vector<int>::reverse_iterator backward = buffer.rbegin();
++forward;
++backward;
std::vector<int>::iterator forwardFromBackward = std::vector<int>::iterator(backward); // error! Can't convert from reverse_iterator to iterator!
std::vector<int>::reverse_iterator backwardFromForward = std::vector<int>::reverse_iterator(forward); // this is fine
You could write a helper function. One particularity of reverse_iterator is that base() gives a forward iterator that is next from the value that the reverse iterator dereferences to. This is because a reverse iterator physically points to the element after the one it logically points to. So to have the forward iterator to the same item as your reverse_iterator, you'll need to decrement the result of base() by one, or you could increment the reverse iterator first, then take the .base() of that.
Both examples are shown below:
#include <iostream>
#include <vector>
#include <iterator>
//result is undefined if passed container.rend()
template <class ReverseIterator>
typename ReverseIterator::iterator_type make_forward(ReverseIterator rit)
{
return --(rit.base()); // move result of .base() back by one.
// alternatively
// return (++rit).base() ;
// or
// return (rit+1).base().
}
int main()
{
std::vector<int> vec(1, 1);
std::vector<int>::reverse_iterator rit = vec.rbegin();
std::vector<int>::iterator fit = make_forward(rit);
std::cout << *fit << ' ' << *rit << '\n';
}
Warning: this behavior is different from that of the reverse_iterator(iterator) constructor.
It's very common to have two (reverse) iterators span a range of values (such as in begin(),end() and rbegin(),rend()). For any range described by the two reverse iterators rA,rB, the range rB.base(),rA.base() will span the same range in the forward direction.
#include <iostream>
#include <iterator>
#include <vector>
int main() {
std::vector<int> vec{10,11,12,13,14,15};
// spans the range from 13 to 10
auto rfirst=std::rbegin(vec)+2;
auto rlast=std::rend(vec);
// Loops forward, prints 10 11 12 13
for(auto it = rlast.base(); it != rfirst.base(); ++it){
std::cout << *it << " ";
}
}
If conceptually you are only interested in a single item (such as the result of find_if), then use make_forward by #visitor. Even in this case, the range idea helps to keep track of the validity of the reverse iterator:
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec{10,11,12,13,14,15};
auto rfirst=std::rbegin(vec);
auto rlast=std::rend(vec);
auto rfound = std::find_if(rfirst,rlast, [](int v){ return v<13; });
if(rfound != rlast){
std::cout << *rfound << " "; // prints 12
auto forwardFound = make_forward(rfound) ;
std::cout << *forwardFound << " "; // prints 12
}
}
You can get forward iterator from reverse iterator using this code
container.begin() + (reverseIter - container.rbegin() - 1);