How to insert into std::map - c++

In code below:
map<string,vector<int>> create(ifstream& in, const vector<string>& vec)
{
/*holds string and line numbers into which each string appears*/
typedef map<string,vector<int>> myMap;
typedef vector<string>::const_iterator const_iter;
myMap result;
string tmp;
unsigned int lineCounter = 0;
while(std::getline(in,tmp))
{
const_iter beg = vec.begin();
const_iter end = vec.end();
while (beg < end)
{
if ( tmp.find(*beg) != string::npos)
{
result[*beg].push_back(lineCounter);//THIS IS THE LINE I'M ASKING FOR
}
++beg;
}
++lineCounter;
}
return result;
}
How should I do it (check line commented in code) if I want to use insert method of map instead of using operator[]?
Thank you.

Seriously, I would not do it.
You are only going to complicate your code unnecessarily. You would need a call to the insert to generate the new element in the map and then modify it.
Just for the sake of it (avoiding the double lookup, but building an unnecessary empty vector):
result.insert( std::make_pair( *beg, std::vector<int>() ) )
.first->second.push_back( lineCounter );
EDIT: Real equivalent (functionality and performance):
std::map<std::string,std::vector<int> >::iterator it = result.upper_bound( *beg );
if ( it->first != *beg ) {
it = result.insert( it, std::make_pair( *beg, std::vector<int>() ) ).first;
}
it->second.push_back( lineCounter );

result.insert(pair<string,vector<int>>(*beg,100), vector<int>());
result[*beg].push_back(lineCounter);
This is more complicated (but slower too :-) than your current code, since that achieves two things with a single statement: (implicitly) inserts an empty array into the map, then adds a new element to the array.

map::insert returns a pair containing an iterator to the element (either the one just inserted or the existing one with that key) and a boolean indicating success or failure. You can then call iter->push_back(lineCounter) to add the new line number to the vector.
...and when you're done with all that, realize that this is exactly what operator[] does for you.

Related

Keep duplicate elements separately

I have a std::vector<std::string> textLines that contains a large number of, say city names. I remove the duplicates with:
using namespace std;
vector<string>::iterator iter;
sort(textLines.begin(), textLines.end());
iter = unique(textLines.begin(), textLines.end());
At this point the duplicate elements are all null (empty) strings at the end of the vector with the same size as before unique().
I remove them with:
textLines.resize(distance(textLines.begin(), iter));
This works OK but is there a way to keep the removed duplicates? It would be better (for me) if the duplicates were just moved to the end and not replaced by empty strings.
The new end is pointed out by iter, returned from unique() so there is no problem finding the new end of the vector.
Put another way, I want to know which lines had duplicates and which had not.
You can do this very simply without actually changing your logic drastically.
You can store duplicates in another container, that is captured by the comparison predicate passed to unique():
vector<string> duplicates;
auto iter = unique(textLines.begin(), textLines.end(), [&duplicates](auto& first, auto& second) -> bool {
if (first == second)
{
duplicates.push_back(second);
return true;
}
return false;
});
Live example: here.
With this solution you need additional memory to store elements count.
vector<string>::iterator iter;
vector<string> v{ "a", "b", "a", "t", "a", "g", "t" };
sort(v.begin(), v.end());
// Find number of distinct elements
int count = 1;
auto current = v.cbegin();
for (auto i = v.cbegin() + 1; i < v.cend(); ++i) {
if (*i != *current) {
++count;
current = i;
}
}
// Count every entry
vector<int> vCount(count);
auto currentCount = vCount.begin();
++*currentCount;
for (size_t i = 1; i < v.size(); ++i) {
if (v[i] == v[i-1]) ++*currentCount;
else *++currentCount = 1;
}
iter = unique(v.begin(), v.end());
You could always write your own function, which is advisable for cases like yours where you have a specific request. Something like:
//Define a "bool has(const vector &v, int element)" function before
vector<string> nonDuplicates;
vector<string> duplicates;
for (auto i : textList) {
if (has(nonDupicates, i)) {
duplicates.push(i);
}
else {
nonDuplicates.push(i);
}
}
This is not a very elegant, or fast, way to do it, so you can probably find a better way, but if you do do it this way, use a binary search for has(), if you have sorted it

vector erase expects 2 arguments error

I am using Code::Blocks and here is my code below in c++.
void removeEvenLength(vector<string> &vec)
{
for (vector<string>::reverse_iterator i = vec.rbegin(); i != vec.rend(); ++i)
{
string word = *i;
if (word.length() % 2 == 0)
vec.erase(i);
}
}
This function takes a vector of strings and removes the word that is even letters long. I get an error that says erase needs 2 arguments but according to the c++ documentation, one should be okay. Do you know what is wrong?
If you absolutely must use erase() with a reverse iterator, here's how:
void removeEvenLength(vector<string> &vec)
{
for (vector<string>::reverse_iterator i = vec.rbegin(); i != vec.rend();)
{
string word = *i;
if (word.length() % 2 == 0) {
vector<string>::iterator newi = vec.erase(i.base() - 1);
i = vector<string>::reverse_iterator(newi);
} else {
++i;
}
}
}
But that's not the best way to remove all even-length strings in a vector, for a number of reasons. The way to do that is as follows (it's called the "erase-remove" idiom):
struct IsEven {
bool operator()(const string &s) const {
return (s.size() % 2) == 0;
}
};
vec.erase(
std::remove_if(vec.begin(), vec.end(), IsEven()),
vec.end()
);
Or in C++11 you can use a lambda instead of a separate functor:
vec.erase(
std::remove_if(
vec.begin(), vec.end(),
[](const string &s) { return (s.size() % 2) == 0; }
),
vec.end()
);
If you see e.g. this reference you will see that erase takes an iterator or a const_iterator, not a reverse_iterator. So you can't use it when iterating backwards.
Also, as suggested by chris in a comment, you might want to use std::remove_if instead. The linked reference contains an example how to use it.

How to erase elements (or a distance-between A and B) in the a vector

I would like to check a vector if its elements have both of integers A and B, delete the elements between them and copy to another vector. for instance, there two vector; vector<> path and vector<> v1
Path v1
---A***B## ---AB##
so the task is deleting the elements between A and B
here is a C++ code but unfortunately it is not working.Is there any idea about what is the problem ?
vector< > Path,v1;
vector<int>::iterator it2,it3;
int A,B;
it2=find(Path.begin(), Path.end(), A) ;
it3=find(Path.begin(), Path.end(), B) ;
vector<int> v1(Path.begin(),Path.end());
if (it2 != Path.end() && it3 != Path.end())
{
if(it2<it3)
{
v1.erase(it2+1,it3);
}
else
{
v1.erase(it3+1,it2);
}
}
First of all, you are creating a new v1 inside your if block, but I'm pretty sure you want to assign to the one that is already there. You're also passing iterators from the Path vector to the erase function of v1, causing undefined behavior. I would also save the work done by v1.erase by not adding the unwanted elements in the first place.
if (it2 != Path.end() && it3 != Path.end())
{
if(it3<it2)
{
std::swap(it2,it3);
}
++it2; // simplifies logic later
// optional step to possibly save an allocation
size_t total = std::distance(Path.begin(), it2);
total += std::distance(it3,Path.end());
v1.reserve(total);
v1.assign(Path.begin(), it2);
v1.insert(v1.end(), it3, Path.end());
}
Note that if A==B, this adds that element twice. I'm not sure if this is desired behavior, but it fits with your problem description.
First of all there is no need to traverse the vector two times from its beginning, Instead of using two times algorithm std::find you could use combination of algorithms std::find_first_of and std::find
For example
int a[] = { A, B }; // where A and B some integer values
std::vector<int>::iterator first, last;
first = last = path.end();
first = std::find_first_of( path.begin(), path.end(), std::begin( a ), std::end( a ) );
if ( first != path.end() )
{
last = std::find( std::next( first ), path.end(), *first == a[0] ? a[1] : a[0] );
}
Now that to move elements in the given range you could write
std::vector<int> v1;
if ( first != path.end() && last != path.end() )
{
++first;
v1.assign( first, last );
path.erase( first, last );
}
Or if the original vector shall not be changed then you could write
std::vector<int> v1;
if ( first != path.end() && last != path.end() )
{
++first;
v1.assign( path.begin(), first );
v1.insert( v1.end(), last, path.end() );
}

c++:Searching in a vector

I'm trying to create a pricing system for computers and I want to create two vectors, one stores item names, and the other one, prices for the item. My plan is to have two methods, "find_item",that finds the item name inside of a vector and returns its index, and "get_itemPrice", that takes the index from find_item and gets the price. My problem is coming up with a code that takes a string object inside of a vector and returns its index position.
You can simply use std::find. It will return an iterator to the first element equal to the one you are searching for, or to the end() if none is found. You can then use std::distance to get an index from that, if you really need it.
Like this:
vector<int> vect;
int i = 0;
for (vector<int>::iterator iter = vect.begin(); iter != vect.end(); ++iter)
{
if (*iter == vect)
break;
i++;
}
if (i == vect.size())
// not found
else
// i is your index
if you really would like to make an algorithm do it like this, but generally one should not try to beat those in the stl see the algorithm header
unsigned find_str( const vector<string>& vec, const string& key ){
unsigned count = 0;
vector<string>::const_iterator it;
for ( it = vec.begin() ; it < vec.end(); it++ ){
if( *it == key)
return count;
count++;
}
/*throw not found error here.*/
}

Copying C++ Map into key and value vectors

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