I have a map and I want the first column i.e (*it).first to be pushed back into a vector then (*it)->second to be pushed back into another vector
Is this the best way to do it?
std::vector<std::string>test;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
}
My other question is if i have a loop i.e
how would I insert all the integers i into (*it).first?
for(int i = 0; i < 10; i++)
{
// 1 - 10 will go in (*it).first
}
I want to have some integers in (*it).first and have associated values in (*it).second;
Use std::transform.
First define two functions key and value which take the pair of strings and return the first or second value, respectively.
#include <map>
#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>
const std::string& key(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.first;
}
const std::string& value(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.second;
}
Then use std::transform from <algorithm> with the functions to transform the map into either a vector of keys or a vector of values.
int main()
{
using namespace std; // be explicit normally, trying to be brief here
map<string, string> contacts;
contacts["alice"] = "555-2701";
contacts["bob"] = "555-2702";
vector<string> keys(contacts.size());
vector<string> values(contacts.size());
transform(contacts.begin(), contacts.end(), keys.begin(), key);
transform(contacts.begin(), contacts.end(), values.begin(), value);
cout << "Keys:\n";
copy(keys.begin(), keys.end(), ostream_iterator<string>(cout, "\n"));
cout << "\n";
cout << "Values:\n";
copy(values.begin(), values.end(), ostream_iterator<string>(cout, "\n"));
return 0;
}
Output:
Keys:
alice
bob
Values:
555-2701
555-2702
Your first question, "how can I push the first column of my map into one vector and the 2nd column into another" is solved thus:
std::map<std::string, std::string> mymap;
std::vector<std::string> keys;
std::vector<std::string> values;
for ( std::map<std::string,std::string>::iterator it=mymap.begin() ; it != mymap.end(); ++it )
{
keys.push_back(it->first);
values.push_back(it->second);
}
Your second question, "how would insert all the integers i into (*it).first ?" is solved thus:
std::map<int, int> mymap2;
for(int i = 0; i < 10; i++)
{
// Insert default value into map
// This sets '(*it).first' to 'i' and
// '(*it).second' to a default value (in
// this case, 0).
mymap2[i];
}
or
std::map<int, int> mymap3;
for(int i = 0; i < 10; i++)
{
// Insert specified value into map
// this sets '(*it).first' to 'i', and
// '(*it).second' to the value returned from the function.
maymap3[i] = ChooseSpecificValue(i);
}
Well, it can be done with a simple loop:
for (auto const& p: mymap) {
vec1.push_back(p.first);
vec2.push_back(p.second);
}
Or using the std::transform algorithm, though it's quite verbose here:
std::transform(mymap.begin(), mymap.end(), std::back_inserter(vec1),
[](MyMap::const_reference p) { return p.first; });
Assuming you've declared your map as string key and value (ie map<string, string> mymap; then it would be like below, also assuming you've declare 'it' variable as map<string, string>::iterator it, etc:
std::vector<std::string> test;
std::vector<std::string> second;
std::map<string, string>::iterator it;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
second.push_back((*it).second);
}
Not sure about your next question.
The first part of your question:
std::vector<std::string> test;
std::vector<std::string> test2; // assuming map is from string to string
for (it = mymap.begin(); it != mymap.end(); ++it)
{
test.push_back(it->first); // push first in one vector
test2.push_back(it->second); // push second in another vector
}
So, yes a simple for can do what you want.
The second part of your question:
Since you are updating the key of the map, you would need to remove it from the map and insert the changed one. So:
std::string first, second;
first = it->first;
second = it->second;
mymap.erase(it); // be careful with invalidating iterator
// change first
mymap[first] = second;
To change first by adding all integers i to it, that would really depend on the type of first. For example with a string, you may mean something like this:
ostringstream sout;
for (int i = 0; i < 10; ++i)
sout << (i?" ":"") << i;
first = sout.str();
Or if first is for example a set, you may mean something like this:
for (int i = 0; i < 10; ++i)
first.insert(i);
and my other question is if i have a loop i.e how would insert all the
integers i into (*it).first?
In the case of a std::map, you can't modify the iterator returned like that ... the key member (i.e., the first) in the std::map key/value pair data-structure is intentionally designated as a constant value, and is initialized to its constant value at the beginning of the key/value pair's lifetime in the std::map data-structure. If the keys weren't constant, you would end up creating havoc when you change the key, since the nodes in a std::map are suppose to be sorted by the keys. The second member of the key/value pair data-structure is the member that can be changed.
So if you want to insert a set of key/value pairs in a map, you could simply do the following:
std::map<int, int> mymap;
int some_other_value = 100;
for (int i=0; i < 10; i++)
{
mymap[i] = some_other_value++;
}
it here will be an iterator which will point to one of the position in map and at max have one first and second value for one iterator . At max you can have multiple key or same key holding same/different values depending on key/value combination.
As far as pushing the value in the vector for a key in map is concern you can do it in the same way you are pushing the key
std::vector<std::string>test;
std::vector<std::string>test2;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
test2.push_back((*it).second);
}
Neways yours question is very unclear .
Just in case you want to deal with different data types in your map I would template a generic copy function:
template <class A, class B>
void mycopy(std::map<A, B>&m, std::list<A>& keys, std::list<B>& values) {
typename std::map<A, B>::iterator it;
for (it = m.begin(); it != m.end(); ++it) {
keys.push_back( (*it).first );
values.push_back( (*it).second );
}
}
Mixing it up:
std::map<int, std::string> mymap;
std::list<int> keys;
std::list<std::string> values;
mymap[1] = "string1";
mymap[2] = "string2";
mycopy(mymap, keys, values);
std::map<std::string, int> mymap1;
std::list<std::string> keys1;
std::list<int> values1;
mymap1["string1"] = 1;
mymap1["string2"] = 2;
mycopy(mymap1, keys1, values1);
Edit: yes __copy isnt the best definition. Thanks
Related
In c++ I need to search a vector containing a pair, in reverse, by the string. I cannot use a map because the strings are not unique and order is important. I then want to return a forward iterator if the string is found or the end iterator if the string is not found.
Please see below for my current code. I have no problem when the string is found but, if the string is not found, I get a segfault in the conditional statement in main.
vector<pair<string, int>>::iterator prev_it(const string& pred,
vector<pair<string, int>> prevpreds) {
vector<pair<string, int>>::reverse_iterator rit;
for(rit = prevpreds.rbegin();
rit != prevpreds.rend(); ++rit) {
if (rit->first == pred) {
return (rit+1).base();}
}
if(rit == prevpreds.rend()) {
return prevpreds.end();
}
}
and in main:
int main() {
vector<pair<string, int>> test;
for(int i = 0; i <= 5; ++i) {
pair<string, int> mypair;
mypair = make_pair("X"+to_string(i%4+1), i+1);
test.emplace_back(mypair);
}
string tpred = "X"+to_string(6);
vector<pair<string, int>>::iterator tit;
tit = prev_it(tpred, test);
if (tit != test.end()) {
cout << tit->first << " " << tit->second << endl;
}
else {cout << "This is the end." << endl;}
}
The code works if tpred is one of X1 to X4. If tpred is X6 (i.e. not an element of test) then I get a segfault. What I would like to be able to do is return the end forward iterator and then, as in main(), have a conditional based on this.
Edit: I am new to c++ (about a year). I am returning a forward iterator because I need to use the iterator later and this seems clearer (but I could be wrong). As far as I understand, a multimap allows non-unique keys but will order the unique keys. I should have been clearer and said time order was important, not key order. I prefer not to use auto while developing because I like to see what I container element/iterator I am using, but point taken.
You're using an iterator of a destructed object. Pass prevpreds by reference, so the iterator maintains valid.
vector<pair<string, int>>::const_iterator prev_it(const string& pred,
const vector<pair<string, int>> &prevpreds)
{
vector<pair<string, int>>::const_reverse_iterator rit;
for (rit = prevpreds.rbegin();
rit != prevpreds.rend(); ++rit)
{
if (rit->first == pred)
{
return (rit + 1).base();
}
}
return prevpreds.end();
}
int main()
{
// ...
vector<pair<string, int>>::const_iterator tit; // <-- uses const iterator
tit = prev_it(tpred, test);
// ...
}
I am currently working on a coding question:
Given an array of strings, return all groups of strings that are anagrams.
For example, Given:
{asch, scah, bva, vba, soa}
return
{ {asch, scah}, {bva, vba}, {soa}}
To solve this question less than O(n^2) time, we should firstly sort each word, and group the sorted words in one set, if the sorted words are the same.
I wanted to use two-dimensional hashmap.
map<string, map<int,string>> container;
to use this two-dimensional hashmap, the first key is the sorted word, the second key is its index in the original sequence, and the value is the original word.
for(int i=0; i<sequence.size();i++)
{
string original_word = sequence[i];
string sorted_word = original_word;
sort(sorted_word.begin(),sorted_word.end());
container[sorted_word][i] = original_word;
}
After this loop, I believe all the anagrams which must have the same sorted_word, will be grouped into the first level of hashmap.
My question is, how should I write the code in order to get the set which has the same sorted_word?
Can I do
for( iterator itr = container.begin(); itr != container.end(); itr++)
{
auto grouped_words = itr.second(); // what is the data type of grouped_word here?
}
correct me if there is anything wrong. Thanks.
I think there's a mistake here:
vector<string, vector<int,string>> container; // ???
As in your question you speak about hash maps, I suppose that you meant:
unordered_map<string, unordered_map<int,string>> container;
In this case, you can use the result as follows:
for( auto itr = container.begin(); itr != container.end(); itr++)
{
auto &grouped_words = itr->second; // prefer a reference
cout << itr->first<<": ";
for (auto &x : grouped_words) {
cout << "\t" << x.first << ":"<< x.second<<endl;
}
}
Here a live demo.
Edit: grouped_words is (here) a reference to an unordered_map<int, string>
I'm new to stl in c++ and I want to iterate over a vector of maps. Then, if one of these maps satisfies a certain condition, I want to make a copy of this map an then insert the copy into the vector.
For example:
int main(){
vector<map<string, int> > my_vector;
map<string, int> my_map;
my_map["zero"] = 0;
my_vector.push_back(my_map);
for(vector<map<string, int> >::iterator iter1 = my_vector.begin();
iter1 != my_vector,end();
iter1++){
for(map<string, int>::iterator iter2 = (*iter1).begin();
iter2 != (*iter1).end();
iter2++){
if(iter2->second == 0){
// make a copy of the map (*iter1), make some changes on it,
// then insert the copy in the vector
}
}
}
return 0;
}
I have tried with:
map<string, int> new_map = *iter1;
new_map["zero"]++; // to avoid an infinite loop
my_vector.push_back(new_map);
But the program crashes. No compiler error, only a program crash.
Then I found this question and answers in stackoverflow, so I tried another way like this:
int main(){
vector<map<string, int> > my_vector;
map<string, int> my_map;
my_map["zero"] = 0;
my_vector.push_back(my_map);
int remaining = my_vector.size();
int current_position = 0;
while(remaining>0){
for(map<string, int>:: iterator iter1 = my_vector[index].begin();
iter1 != my_vector[index].end();
iter1++){
if(iter1->second == 0){
map<string, int> new_map = my_vector[0];
new_map["zero"]++; // to avoid an infinite loop
my_vector.push_back(new_map);
}
}
index++;
remaining = my_vector.size()-index;
}
return 0;
}
But the program is still crashing, so I think the problem (or one of the problems) would be not only the iterator, but also the "copy" of the map.
If anyone have an idea of how I should do this, I will really appreciate it.
Your program may crash because push_back can invalidate iterators. For example if a call to push_back leads to memory reallocation (it happens when you exceed current capacity of the vector) then all vector's iterators become invalid (they point to deallocated memory).
To solve that problem you can use indexes instead of iterators to access the vector's elements or you can push_back new elements to another copy of the vector.
In other words: don't use an old vector::iterator after you push_back'ed a new element into the vector.
One way you can do is:
size_t orig_size = my_vector.size();
for( size_t i = 0; i < orig_size; i++ ) {
//...
}
Other way:
Build a new vector and then afterwards append the contents of this new vector to the original vector.
I probably have the wrong idea, but I think you can do something like this. You iterate over the elements of the vector, use the predicate to see if a current element fits the criteria, then use back_inserter.
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
int main(){
vector<map<string, int> > my_vector;
map<string, int> my_map;
my_map["zero"] = 0;
my_vector.push_back(my_map);
auto predicate = [](const map<string, int>& cur) {
for (const auto& pair : cur)
{
if (pair.second == 0)
return true;
}
return false;
};
std::copy_if(my_vector.begin(), my_vector.end(), back_inserter(my_vector), predicate);
return 0;
}
This is my solution. It uses C++11 (because it looks so much better). Especially with collections C++11 gives the workflow a real boost.
#include <vector>
#include <string>
#include <map>
#include <iostream>
using namespace std ;
void merge(vector<map<string,int>>& from, vector<map<string,int>>& to)
{
for(auto entry : from) {
to.push_back(entry) ;
}
}
void search(vector<map<string,int>>& vec)
{
int iCount = 0 ;
vector<map<string, int>> cVector ;
for(auto vmap : vec) {
for(auto mapentry : vmap) {
if(mapentry.second == 0) {
iCount++ ;
map<string, int> new_map = vmap ;
cVector.push_back(new_map) ;
}
}
}
cout << "iCount: " << iCount << endl ;
merge(cVector, vec) ;
}
int main(){
vector<map<string, int> > my_vector;
map<string, int> my_map;
my_map["zero"] = 0;
my_map["third"] = 2 ;
my_map["second"] = 0 ;
my_vector.push_back(my_map);
cout << my_vector.size() << endl ;
vector<map<string,int>> cVector ;
search(my_vector) ;
cout << my_vector.size() << endl ;
return 0;
}
As written by Alex Antonov the push_back invalidates your Iterator because the memory gets reallocated. My answer creates a new vector and copies the entries after the search back in the original vector (my_vector).
About range base for-loops see this.
I'm trying to print out the five most used values. But when I changed my map to a multimap I broke the code where I add values to the map. How can I add values to a multimap? Can it be done in a similar way as I add values to a map?
// Create a map for keeping track of how many occurences of the different colors
multimap<string, int> hexmap;
// Add the hex values to the map
for(int i = 0; i < imagesize; i ++)
{
hexmap[colors[i]]++;
}
typedef std::multimap<int, string> Mymap;
Mymap dst;
std::transform(hexmap.begin(), hexmap.end(),
std::inserter(dst, dst.begin()),
[](const std::pair<string,int> &p )
{
return std::pair<int, string>(p.second, p.first);
}
);
Mymap::iterator st = dst.begin(),it;
size_t count = 5;
for(it = st; ( it != dst.end() ) && ( --count ); ++it)
std::cout << it->second << it->first << endl;
You add elements to a std::multimap<K, V> using insert() or emplace(), e.g.:
std::multimap<std::string, int> map;
map.insert(std::make_pair("hello" , 1));
map.insert({ "world", 0 });
map.emplace("hello", 0);
You'd locate objects in the std::multimap<K, V> using it's find() member, e.g.:
std::multimap<std::string, int>::iterator it = map.find("hello");
"I'm trying to print out the five most used values."
In this case, you don't have to use hexmap as std::multimap just std::map will do the job
However std::multimap for dst should be required
I have a function already that takes out the key value with the most mapped value.
// Function for finding the occurances of colors or in this case hex values
void findOccurrances(double * mostNumTimes, map<string, int> &hexmap, string * colorlist)
{
map<string,int>::iterator it = hexmap.begin();
for( ;it != hexmap.end(); it ++)
{
if(*mostNumTimes <= it->second)
{
*mostNumTimes = it->second;
*colorlist = it->first;
}
}
}
Is there an easy way to expand it to show the top five results?
I know you can copy it to a vector and what not but I'm wanting an easier way of doing it.
Copying into a vector isn't that difficult:
typedef std::pair<string, int> Pair;
std::vector<Pair> contents(hexmap.begin(), hexmap.end());
Done.
But to find the top 5, believe it or not <algorithm> has a function template that does exactly what you want. In C++11, which usefully has lambdas:
std::vector<Pair> results(5);
std::partial_sort_copy(
hexmap.begin(), hexmap.end(),
results.begin(), results.end(),
[](const Pair &lhs, const Pair &rhs) { return lhs.second > rhs.second; }
);
results now contains the top 5 entries in descending order.
I have a little bit confused about the arguments. why you write the function in this way, And I try for this.
string colorlist;
double max_val = 0;
for (int i = 0; i < 5; ++i)
{
findOccurrances(&max_val, hexmap, &colorlist);
cout << colorlist << " " << max_val << endl; // to do something
mapStudent.erase(colorlist);
}
I'd interchange the key and value and create new map
std::map<int,std::string> dst;
std::transform(hexmap.begin(), hexmap.end(),
std::inserter(dst, dst.begin()),
[](const std::pair<std::string,int> &p )
{
return std::pair<int,std::string>(p.second, p.first);
}
);
And now print top five values of dst in usual way,
typedef std::map<int, std::string> Mymap;
Mymap::iterator st = dst.begin(), it;
size_t count = 5;
for(it = st; ( it != dst.end() ) && ( --count ); ++it)
std::cout << it->second << it->first <<std::endl ;
Edit:
Use a std::multimap if there are same int (value) for more than one std::string (key) in your hexmap