I am trying to write a program that takes lines from an input file, sorts the lines into 'signatures' for the purpose of combining all words that are anagrams of each other. I have to use a map, storing the 'signatures' as the keys and storing all words that match those signatures into a vector of strings. Afterwards I must print all words that are anagrams of each other on the same line. Here is what I have so far:
#include <iostream>
#include <string>
#include <algorithm>
#include <map>
#include <fstream>
using namespace std;
string signature(const string&);
void printMap(const map<string, vector<string>>&);
int main(){
string w1,sig1;
vector<string> data;
map<string, vector<string>> anagrams;
map<string, vector<string>>::iterator it;
ifstream myfile;
myfile.open("words.txt");
while(getline(myfile, w1))
{
sig1=signature(w1);
anagrams[sig1]=data.push_back(w1); //to my understanding this should always work,
} //either by inserting a new element/key or
//by pushing back the new word into the vector<string> data
//variable at index sig1, being told that the assignment operator
//cannot be used in this way with these data types
myfile.close();
printMap(anagrams);
return 0;
}
string signature(const string& w)
{
string sig;
sig=sort(w.begin(), w.end());
return sig;
}
void printMap(const map& m)
{
for(string s : m)
{
for(int i=0;i<m->second.size();i++)
cout << m->second.at();
cout << endl;
}
}
The first explanation is working, didn't know it was that simple! However now my print function is giving me:
prob2.cc: In function âvoid printMap(const std::map<std::basic_string<char>, std::vector<std::basic_string<char> > >&)â:
prob2.cc:43:36: error: cannot bind âstd::basic_ostream<char>::__ostream_type {aka std::basic_ostream<char>}â lvalue to âstd::basic_ostream<char>&&â
In file included from /opt/centos/devtoolset-1.1/root/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/iostream:40:0,
Tried many variations and they always complain about binding
void printMap(const map<string, vector<string>> &mymap)
{
for(auto &c : mymap)
cout << c.first << endl << c.second << endl;
}
anagrams[sig1] will return a reference to a vector<string>. Rather than assign to it, you just want to push_back onto it.
sig1 = signature(w1);
anagrams[sig1].push_back(w1);
As your code is written right now, it's trying to replace the vector instead of add to it. For example, let's assume your input contains both was and saw, and that your signature sorts the letters of the string.
What you want for this case is:
read "was"
sort to get "asw"
insert "was" to get: anagrams["asw"] -> ["was"]
read "saw"
Sort to get "asw" (again)
insert "saw" to get: anagrams["asw"] -> ["was", "saw"]
With the code as you've tried to write it, however, in step 6, instead of adding to the existing vector, you'd overwrite the current vector with a new one containing only "saw", so the result would be just anagrams["asw"] -> ["saw"].
As far as printmap goes: the items in the map aren't std::strings, they're std::pair<std::string, std::vector<std::string>>, so when you try to do:
void printMap(const map& m)
{
for(string s : m)
...that clearly can't work. I'd usually use:
for (auto s : m)
...which makes it easy to get at least that much to compile. To do anything useful with the s, however, you're going to need to realize that it's a pair, so you'll have to work with s.first and s.second (and s.first will be a string, and s.second will be a std::vector<std::string>). To print them out, you'll probably want to print s.first, then some separator, then walk though the items in s.second.
Related
I am using std::find to check a string isn't in std::vector<std::vector<string>>
Error:
no match for 'operator==' (operand types are 'std::vector<std::__cxx11::basic_string<char> >' and 'const char [6]')
Isn't it the type doesn't match?
vector< vector< string>>data;
if(find(data.begin(), data.end(), "START") == data.end()){
printf("Missing \"START\"\n");
return true;`
The reason for the error message has been well explained in the other answer. I would like to provide a solution to the problem.
As you are trying to find, if any of the std::string element in the vector of vector matches to "START", you could use standard algorithm std::any_of in combination with a unary predicate which returns std::find(vec.cbegin(), vec.cend(), str) != vec.cend(); where vec is the each rows of the vector of vectors. See a demo here
#include <algorithm>
#include <string>
#include <iostream>
#include <vector>
bool isFound(const std::vector<std::vector<std::string>>& data, const std::string &str)
{
const auto found_in_vector = [&str](const std::vector<std::string> & vec)-> bool {
return std::find(vec.cbegin(), vec.cend(), str) != vec.cend(); // if found
};
if (std::any_of(data.cbegin(), data.cend(), found_in_vector))
{
std::cout << "Found\n";
return true;
}
std::cout << "Missing \""<< str << " \"\n";
return false;
}
int main()
{
std::vector<std::vector<std::string>> data;
std::vector<std::string> test{ "START","test" };
data.emplace_back(test);
std::cout << std::boolalpha << isFound(data, std::string{ "START" } );
}
Yes and no. The error is triggered, because you've got a "vector of vectors of strings", i.e. there's one dimension too much. Define data using std::vector<std::string> instead and it will work.
But why does the error talk about a missing operators?
When you use std::find(), it's typically implemented as a macro or templated function, which does the actual work, rather than a precompiled runtime function somewhere in a library. This allows the compiler full optimization based on the actual types of your parameters.
What it actually does - since your container is a class - is trying to find a special member function, std::vector<std::vector<std::string>>::operator==(const char*). It's not directly implemented this way, typically a template instead, but that's not important here. The important fact is it won't find any version of operator==() with an argument that is somehow able to accept the string passed, either directly or through conversion. Reason for this is that your vector contains vectors, so the only valid argument would be another vector of strings.
This is my first time implementing map in C++. So given a character array with text, I want to count the frequency of each word occurring in the text. I decided to implement map to store the words and compare following words and increment a counter.
Following is the code I have written so far.
const char *kInputText = "\
So given a character array with text, I want to count the frequency of
each word occurring in the text.\n\
I decided to implement map to store the\n\
words and compare following words and increment a counter.\n";
typedef struct WordCounts
{
int wordcount;
}WordCounts;
typedef map<string, int> StoreMap;
//countWord function is to count the total number of words in the text.
void countWord( const char * text, WordCounts & outWordCounts )
{
outWordCounts.wordcount = 0;
size_t i;
if(isalpha(text[0]))
outWordCounts.wordcount++;
for(i=0;i<strlen(text);i++)
{
if((isalpha(text[i])) && (!isalpha(text[i-1])))
outWordCounts.wordcount++;
}
cout<<outWordCounts.wordcount;
}
//count_for_map() is to count the word frequency using map.
void count_for_map(const char *text, StoreMap & words)
{
string st;
while(text >> st)
words[st]++;
}
int main()
{
WordCounts wordCounts;
StoreMap w;
countWord( kInputText, wordCounts );
count_for_map(kInputText, w);
for(StoreMap::iterator p = w.begin();p != w.end();++p)
{
std::cout<<p->first<<"occurred" <<p->second<<"times. \n";
}
return 0;
}
Error: No match for 'operator >>' in 'text >> st'
I understand this is an operator overloading error, so I went ahead and
wrote the following lines of code.
//In the count_for_map()
/*istream & operator >> (istream & input,const char *text)
{
int i;
for(i=0;i<strlen(text);i++)
input >> text[i];
return input;
}*/
Am I implementing map in the wrong way?
There is no overload for >> with a const char* left hand side.
text is a const char*, not an istream, so your overload doesn't apply (and the overload 1: is wrong, and 2: already exists in the standard library).
You want to use the more suitable std::istringstream, like this:
std::istringstream textstream(text);
while(textstream >> st)
words[st]++;
If you use modern C++ language, then life will get by far easier.
First. Usage of a std::map is the correct approach.
This is a more or less standard approach for counting something in a container.
We can use an associative container like a std::map or a std::unordered_map. And here we associate a "key", in this case the "word" to count, with a value, in this case the count of the specific word.
And luckily the maps have a very nice index operator[]. This will look for the given key and if found, return a reference to the value. If not found, the it will create a new entry with the key and return a reference to the new entry. So, in bot cases, we will get a reference to the value used for counting. And then we can simply write:
std::unordered_map<std::string, unsigned int> counter{};
counter[word]++;
But how to get words from a string. A string is like a container containing elements. And in C++ many containers have iterators. And especially for strings there is a dedicated iterator that allows to iterate over patterns in a std::string. It is called std::sregex_token_iterator and described here.. The pattern is given as a std::regex which will give you a great flexibility.
And, because we have such a wonderful and dedicated iterator, we should use it!
Eveything glued together will give a very compact solution, with a minimal number of code lines.
Please see:
#include <iostream>
#include <string>
#include <regex>
#include <map>
#include <iomanip>
const std::regex re{ "\\w+" };
const std::string text{ R"(So given a character array with text, I want to count the frequency of
each word occurring in the text.
I decided to implement map to store the
words and compare following words and increment a counter.")" };
int main() {
std::map<std::string, unsigned int> counter{};
for (auto word{ std::sregex_token_iterator(text.begin(),text.end(),re) }; word != std::sregex_token_iterator(); ++word)
counter[*word]++;
for (const auto& [word, count] : counter)
std::cout << std::setw(20) << word << "\toccurred\t" << count << " times\n";
}
So if I have a vector of words like:
Vec1 = "words", "words", "are", "fun", "fun"
resulting list: "fun", "words"
I am trying to determine which words are duplicated, and return an alphabetized vector of 1 copy of them. My problem is that I don't even know where to start, the only thing close to it I found was std::unique_copy which doesn't exactly do what I need. And specifically, I am inputting a std::vector<std::string> but outputting a std::list<std::string>. And if needed, I can use functor.
Could someone at least push me in the right direction please? I already tried reading stl documentation,but I am just "brain" blocked right now.
In 3 lines (not counting the vector and list creation nor the superfluous line-breaks in name of readability):
vector<string> vec{"words", "words", "are", "fun", "fun"};
list<string> output;
sort(vec.begin(), vec.end());
set<string> uvec(vec.begin(), vec.end());
set_difference(vec.begin(), vec.end(),
uvec.begin(), uvec.end(),
back_inserter(output));
EDIT
Explanation of the solution:
Sorting the vector is needed in order to use set_difference() later.
The uvec set will automatically keep elements sorted, and eliminate duplicates.
The output list will be populated by the elements of vec - uvec.
Make an empty std::unordered_set<std::string>
Iterator your vector, checking whether each item is a member of the set
If it's already in the set, this is a duplicate, so add to your result list
Otherwise, add to the set.
Since you want each duplicate only listed once in the results, you can use a hashset (not list) for the results as well.
IMO, Ben Voigt started with a good basic idea, but I would caution against taking his wording too literally.
In particular, I dislike the idea of searching for the string in the set, then adding it to your set if it's not present, and adding it to the output if it was present. This basically means every time we encounter a new word, we search our set of existing words twice, once to check whether a word is present, and again to insert it because it wasn't. Most of that searching will be essentially identical -- unless some other thread mutates the structure in the interim (which could give a race condition).
Instead, I'd start by trying to add it to the set of words you've seen. That returns a pair<iterator, bool>, with the bool set to true if and only if the value was inserted -- i.e., was not previously present. That lets us consolidate the search for an existing string and the insertion of the new string together into a single insert:
while (input >> word)
if (!(existing.insert(word)).second)
output.insert(word);
This also cleans up the flow enough that it's pretty easy to turn the test into a functor that we can then use with std::remove_copy_if to produce our results quite directly:
#include <set>
#include <iterator>
#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
class show_copies {
std::set<std::string> existing;
public:
bool operator()(std::string const &in) {
return existing.insert(in).second;
}
};
int main() {
std::vector<std::string> words{ "words", "words", "are", "fun", "fun" };
std::set<std::string> result;
std::remove_copy_if(words.begin(), words.end(),
std::inserter(result, result.end()), show_copies());
for (auto const &s : result)
std::cout << s << "\n";
}
Depending on whether I cared more about code simplicity or execution speed, I might use an std::vector instead of the set for result, and use std::sort followed by std::unique_copy to produce the final result. In such a case I'd probably also replace the std::set inside of show_copies with an std::unordered_set instead:
#include <unordered_set>
#include <iterator>
#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
class show_copies {
std::unordered_set<std::string> existing;
public:
bool operator()(std::string const &in) {
return existing.insert(in).second;
}
};
int main() {
std::vector<std::string> words{ "words", "words", "are", "fun", "fun" };
std::vector<std::string> intermediate;
std::remove_copy_if(words.begin(), words.end(),
std::back_inserter(intermediate), show_copies());
std::sort(intermediate.begin(), intermediate.end());
std::unique_copy(intermediate.begin(), intermediate.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
This is marginally more complex (one whole line longer!) but likely to be substantially faster when/if the number of words gets very large. Also note that I'm using std::unique_copy primarily to produce visible output. If you just want the result in a collection, you can use the standard unique/erase idiom to get unique items in intermediate.
In place (no additional storage). No string copying (except to result list). One sort + one pass:
#include <string>
#include <vector>
#include <list>
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
vector<string> vec{"words", "words", "are", "fun", "fun"};
list<string> dup;
sort(vec.begin(), vec.end());
const string empty{""};
const string* prev_p = ∅
for(const string& s: vec) {
if (*prev_p==s) dup.push_back(s);
prev_p = &s;
}
for(auto& w: dup) cout << w << ' ';
cout << '\n';
}
You can get a pretty clean implementation using a std::map to count the occurrences, and then relying on std::list::sort to sort the resulting list of words. For example:
std::list<std::string> duplicateWordList(const std::vector<std::string>& words) {
std::map<std::string, int> temp;
std::list<std::string> ret;
for (std::vector<std::string>::const_iterator iter = words.begin(); iter != words.end(); ++iter) {
temp[*iter] += 1;
// only add the word to our return list on the second copy
// (first copy doesn't count, third and later copies have already been handled)
if (temp[*iter] == 2) {
ret.push_back(*iter);
}
}
ret.sort();
return ret;
}
Using a std::map there seems a little wasteful, but it gets the job done.
Here's a better algorithm than the ones other people have proposed:
#include <algorithm>
#include <vector>
template<class It> It unique2(It const begin, It const end)
{
It i = begin;
if (i != end)
{
It j = i;
for (++j; j != end; ++j)
{
if (*i != *j)
{ using std::swap; swap(*++i, *j); }
}
++i;
}
return i;
}
int main()
{
std::vector<std::string> v;
v.push_back("words");
v.push_back("words");
v.push_back("are");
v.push_back("fun");
v.push_back("words");
v.push_back("fun");
v.push_back("fun");
std::sort(v.begin(), v.end());
v.erase(v.begin(), unique2(v.begin(), v.end()));
std::sort(v.begin(), v.end());
v.erase(unique2(v.begin(), v.end()), v.end());
}
It's better because it only requires swap with no auxiliary vector for storage, which means it will behave optimally for earlier versions of C++, and it doesn't require elements to be copyable.
If you're more clever, I think you can avoid sorting the vector twice as well.
I have a data structure which has,
<Book title>, <Author>, and <rate>
Since Book title or Author can be duplicated, I'd like to build a composite key.
(let's say I cannot make extra unique key, such as ID)
Since data is pretty huge, I'm using GCC unordered_map for the sake of speed,
and I built my structure like this:
typedef pair<string, string> keys_t
typedef unordered_map<keys_t, double> map_t;
Everything works okay in general,
But the problem happens when I want to refer one specific key.
For example, let's suppose I'd like to find the best-rated book among books titled as "math", or I'd like to find the average rate of "Tolstoy"'s books.
In this case, this becomes very bothersome, since I cannot only refer only one of the key pair.
I happened to find boost::multi_index but I'm having some trouble understanding the documents.
Does anyone have some idea or guideline for this?
Solution to make multiple indices, succinct example for multi_index, any other approach, etc.. any help will be appreciated.
Thank you.
I figured out how to use boost::multi_index
I referred this code: Boost multi_index composite keys using MEM_FUN
and here's my code for your reference.
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <boost/multi_index/member.hpp>
#include <iostream>
#include <string>
using namespace boost::multi_index;
using namespace std;
class Book {
public:
Book(const string &lang1, const string &lang2, const double &value) : m_lang1(lang1) , m_lang2(lang2) , m_value(value) {}
friend std::ostream& operator << (ostream& os,const Book& n) {
os << n.m_lang1 << " " << n.m_lang2 << " " << n.m_value << endl;
return os;
}
const string &lang1() const { return m_lang1; }
const string &lang2() const { return m_lang2; }
const double &value() const { return m_value; }
private:
string m_lang1, m_lang2;
double m_value;
};
// These will be Tag names
struct lang1 {};
struct lang2 {};
struct value {};
typedef multi_index_container <
Book,
indexed_by<
ordered_non_unique<tag<lang1>, BOOST_MULTI_INDEX_CONST_MEM_FUN( Book, const string &, lang1)
>,
ordered_non_unique<tag<lang2>, BOOST_MULTI_INDEX_CONST_MEM_FUN(Book, const string &, lang2)
>,
ordered_non_unique<tag<value>, BOOST_MULTI_INDEX_CONST_MEM_FUN(Book, const double &, value), greater<double>
>,
ordered_unique<
// make as a composite key with Title and Author
composite_key<
Book,
BOOST_MULTI_INDEX_CONST_MEM_FUN(Book, const string &, lang1),
BOOST_MULTI_INDEX_CONST_MEM_FUN(Book, const string &, lang2)
>
>
>
> Book_set;
// Indices for iterators
typedef Book_set::index<lang1>::type Book_set_by_lang1;
typedef Book_set::index<lang2>::type Book_set_by_lang2;
typedef Book_set::index<value>::type Book_set_by_value;
int main() {
Book_set books;
books.insert(Book("Math", "shawn", 4.3));
books.insert(Book("Math", "john", 4.2));
books.insert(Book("Math2", "abel", 3.8));
books.insert(Book("Novel1", "Tolstoy", 5.0));
books.insert(Book("Novel1", "Tolstoy", 4.8)); // This will not be inserted(duplicated)
books.insert(Book("Novel2", "Tolstoy", 4.2));
books.insert(Book("Novel3", "Tolstoy", 4.4));
books.insert(Book("Math", "abel", 2.5));
books.insert(Book("Math2", "Tolstoy", 3.0));
cout << "SORTED BY TITLE" << endl;
for (Book_set_by_lang1::iterator itf = books.get<lang1>().begin(); itf != books.get<lang1>().end(); ++itf)
cout << *itf;
cout << endl<<"SORTED BY AUTHOR" << endl;
for (Book_set_by_lang2::iterator itm = books.get<lang2>().begin(); itm != books.get<lang2>().end(); ++itm)
cout << *itm;
cout << endl<<"SORTED BY RATING" << endl;
for (Book_set_by_value::iterator itl = books.get<value>().begin(); itl != books.get<value>().end(); ++itl)
cout << *itl;
// Want to see Tolstoy's books? (in descending order of rating)
cout << endl;
Book_set_by_lang2::iterator mitchells = books.get<lang2>().find("Tolstoy");
while (mitchells->lang2() == "Tolstoy")
cout << *mitchells++;
return 0;
}
Thank you all who made comments!
What I did in a similar case was use a single container to contain the
objects and separate std::multiset<ObjectType const*, CmpType> for
each possible index; when inserting, I'd do a push_back, then recover
the address from back(), and insert it into each of the std::set.
(std::unordered_set and std::unordered_multiset would be better in
your case: in my case, not only was the order significant, but I didn't
have access to a recent compiler with unordered_set either.)
Note that this supposes that the objects are immutable once they are in
the container. If you're going to mutate one of them, you probably should
extract it from all of the sets, do the modification, and reinsert it.
This also supposes that the main container type will never invalidate
pointers and references to an object; in my case, I knew the maximum
size up front, so I could do a reserve() and use std::vector.
Failing this, you could use std::deque, or simply use an std::map
for the primary (complete) key.
Even this requires accessing the complete element in the key. It's not
clear from your posting whether this is enough—“books titled
math” makes me thing that you might need a substring search in the
title (and should “Tolstoy” match “Leo
Tolstoy”?). If you want to match an arbitrary substring, either
your multiset will be very, very large (since you'll insert all possible
substrings as entries), or you'll do a linear search. (On a long
running system where the entries aren't changing, it might be worth
compromizing: do the linear search the first time the substring is
requested, but cache the results in a multiset, so that the next time,
you can find them quickly. It's likely that people will often use the
same substrings, e.g. “math” for any book with
“math” in its title.)
There is an article on the same subject:
http://marknelson.us/2011/09/03/hash-functions-for-c-unordered-containers/
The author, Mark Nelson, was trying to do the similar : "use of a simple class or structure to hold the person’s name", basically he's using a pair as key (just like you) for his unordered_map:
typedef pair<string,string> Name;
int main(int argc, char* argv[])
{
unordered_map<Name,int> ids;
ids[Name("Mark", "Nelson")] = 40561;
ids[Name("Andrew","Binstock")] = 40562;
for ( auto ii = ids.begin() ; ii != ids.end() ; ii++ )
cout << ii->first.first
<< " "
<< ii->first.second
<< " : "
<< ii->second
<< endl;
return 0;
}
He realized that the unordered_map doesn’t know how to create a hash for the given key type of std::pair.
So he demonstrates 4 ways of creating a hash function for use in unordered_map.
If it is an infrequent operation you can search for the value.
for(auto& p : m)
{
if(p.second.name==name_to_find)
{
//you now have the element
}
}
however if the map is large this will be problematic because it will be a linear procedure rather than O(log n), this is a problem because maps are inherently slow.
I need to use STL C++ map to store key value pairs.
I need to store more than one data information in stl map.
e.g
Need to store DataType,Data and its behavior as(in param/outparam) all in string format.
But map always use key value pair
so if I
store it like
std::map<map<"int","50",>"behavior">.
But always it sorts the the data on basis of keys which I dont want. If I use like ..
pair<string, pair<string,string> >;
pair<string, pair<string,string>>("int",("100","in"));
This prompts compile time error!
error C2664: 'std::pair<_Ty1,_Ty2>::pair(const std::pair<_Ty1,_Ty2> &)' : cannot convert parameter 1 from 'const char *' to 'const std::pair<_Ty1,_Ty2> &'
What should be the exact solution of the above problem?
Regards
If you don't want ordering, don't use ordered containers like map or set. You could achieve what you're looking for using a class and a vector. The sorted nature of std::map is what makes it efficient to lookup by key. If you want/need unsorted and more hash like behavior for lookups, look at Boost unordered containers. Note that this doesn't guarantee the order in is going to be the order out. I'm assuming you want to preserve the order of the types your putting in the container, the example below will do that.
#include <iostream>
#include <vector>
using namespace std;
class DataBehavior
{
public:
DataBehavior(const string &type,
const string &data,
const string &behavior)
:type(type), data(data), behavior(behavior)
{
}
const string &getType() const { return type; }
const string &getData() const { return data; }
const string &getBehavior() const { return behavior; }
private:
string type;
string data;
string behavior;
};
typedef vector<DataBehavior> Ctr;
int main (int argc, char *argv[])
{
Ctr ctr;
ctr.push_back(DataBehavior("int", "50", "behavior"));
ctr.push_back(DataBehavior("int", "80", "behavior"));
ctr.push_back(DataBehavior("string", "25", "behavior2"));
for (Ctr::const_iterator it = ctr.begin(); it != ctr.end(); ++it)
{
cout << "Type: " << it->getType() << " "
<< "Data: " << it->getData() << " "
<< "Behavior: " << it->getBehavior() << "\n";
}
return 1;
}
You're declaring the variables wrong in the
pair<string, pair<string,string> >;
pair<string, pair<string,string>>("int",("100","in"));
You'd need to do
pair<string, pair<string,string>>("int",pair<string,string>("100","in"));
which is fairly verbose. You could use the make_pair function to significantly shorten that:
make_pair( "int", make_pair( "100", "in" ) );
Also, as the others have said, if you don't want sorted order, use unordered_map from TR1 or Boost. If you're on MS you can use hash_map instead.
Need to store DataType,Data and its
behavior as(in param/outparam) all in
string format. But map always use key
value pair
To me it seems you need to use different data structure, e.g.
typedef std::tuple<std::string, // DataType
std::string, // Data
std::string> element_type; // behaviour
// use can replace tuple with sth like:
//struct element_type {
// std::string m_DataType;
// std::string m_Data;
// std::string m_Behaviour;
// ... ctors, operators ...
//};
// or even with std::array<std::string, 3>
std::set<element_type> elements_set;
// or
typedef int32_t element_id;
std::map<element_id, element_type> elements_map;
And you've got rather a design problem than a programming one.
Why do you use a map in the first place? What do you want to use as lookup key -- the datatype? The value? The "param/outparam" characteristic?
Whatever you want to use as a key should go first, and the rest could be a struct / pointer / string (*urg*) / ... .
So I suggest you give some more thought to your needs and design a datastructure accordingly.