How to initialize a map from a set - c++

I want to set the keys of a std::map using the elements of a std::set (or a std::vector).
Something like the following...
std::set<int> keys = { 3,4,6 };
std::map<int,string> results(keys); // syntax error
Can this be done without explicitly iterating over the set?

You can't. A map is not a set. They're fundamentally different containers, even if the underlying structure is similar.
That said, anything's possible. The range constructor of std::map is linear time if the elements of the range are already sorted, which set guarantees for us. So all you need to do is apply a transformer to every element to produce a new range. The simplest would be to just use something like boost::make_transform_iterator (or roll your own):
template <class K, class F
class V = decltype(std::declval<F&>()(std::declval<K const&>()))::second_type>
std::map<K, V> as_map(std::set<K> const& s, F&& f) {
return std::map<K,V>(
boost::make_transform_iterator(s.begin(), f),
boost::make_transform_iterator(s.end(), f));
}
std::map<int,string> results =
as_map(keys, [](int i){
return std::make_pair(i, std::string{});
});
which if you always will want default initialization, can just reduce to:
template <class V, class K>
std::map<K, V> as_map_default(std::set<K> const& s) {
auto f = [](K const& k) { return std::make_pair(k, V{}); }
return std::map<K,V>(
boost::make_transform_iterator(s.begin(), f),
boost::make_transform_iterator(s.end(), f));
}
std::map<int,string> results = as_map_default<string>(keys);

Can this be done without explicitly iterating over the set?
No. There is no way to know the keys in the set without iterating over it. You may write functions to make it appear as if there is an implicit transformation, but those functions must ultimately iterate the source collection.
A simple way is as follows:
#include <set>
#include <map>
#include <string>
auto build_map(const std::set<int>& source) -> std::map<int,std::string>
{
std::map<int,std::string> results;
for (auto const& i : source) {
results[i];
}
return results;
}
int main()
{
std::set<int> keys = { 3,4,6 };
auto results = build_map(keys);
}
Of course we may templatise if that improves readability:
#include <set>
#include <vector>
#include <unordered_set>
#include <map>
#include <string>
#include <utility>
template<class MappedType, class SourceContainer>
auto build_map(SourceContainer&& source)
{
using source_type = std::decay_t<SourceContainer>;
using key_type = typename source_type::value_type;
std::map<key_type , MappedType> results;
for (auto const& i : source) {
results[i];
}
return results;
}
int main()
{
std::set<int> keys = { 3,4,6 };
auto results = build_map<std::string>(keys);
// also
results = build_map<std::string>(std::vector<int>{3, 4, 6});
results = build_map<std::string>(std::unordered_set<int>{3, 4, 6});
}

Related

sorting map using callback function in sort()

I have a unordered_map<int,int> type variable. I want to sort the map according to its value. I was wondering if I can use callback in sort function, so that it returns sorted map according to it's value.
unordered_map<int,int> p;
for(int i=0;i<nums.size();i++){
p[nums[i]]++;
}
sort(p.begin(),p.end(),callback);
You can't sort a unordered_map in-place. You need to build a sortable alternate container, dump the content there (or bring appropriate material necessary to reference the pairs in the original map), then sort away. There are a multitude of ways to accomplish this. A few examples are shown below.
Value Copy and Sort
The following generates one-hundred random draws in the inclusive domain 1..10, then prints the frequency table after sorting based on frequency (value) descending (the unspoken task I suspect to be driving all of this to begin with):
#include <iostream>
#include <algorithm>
#include <vector>
#include <unordered_map>
#include <random>
int main()
{
std::mt19937 rng{ std::random_device{}() };
std::uniform_int_distribution<> dist(1, 10);
std::unordered_map<int, int> p;
for (int i = 0; i < 100; ++p[dist(rng)], ++i);
std::vector<std::pair<int,int>> v { p.begin(), p.end() };
std::sort(v.begin(), v.end(),
[](auto const& pr1, auto const& pr2)
{ return pr2.second < pr1.second; });
for (auto const& pr : v)
std::cout << pr.first << ':' << pr.second << '\n';
}
Output (varies)
3:14
4:13
7:13
9:11
1:11
2:10
10:7
6:7
8:7
5:7
Pointers to Pairs
An alternate mechanism using constant pointers to the original map pairs is also possible (and probably preferable if the mapped content is either too expensive or outright impossible to make copies of):
#include <iostream>
#include <algorithm>
#include <vector>
#include <unordered_map>
#include <random>
int main()
{
std::mt19937 rng{ std::random_device{}() };
std::uniform_int_distribution<> dist(1, 10);
std::unordered_map<int, int> p;
for (int i = 0; i < 100; ++p[dist(rng)], ++i);
std::vector<const decltype(p)::value_type*> v;
for (auto const& pr : p)
v.emplace_back(&pr);
std::sort(v.begin(), v.end(),
[](auto const& pr1, auto const& pr2)
{ return pr2->second < pr1->second; });
for (auto const& pr : v)
std::cout << pr->first << ':' << pr->second << '\n';
}
Output (varies)
2:16
1:14
3:11
7:11
4:9
10:9
5:8
8:8
9:8
6:6
Caveats apply to this approach. Those pointers to pairs are only as good as the day they were cashed. E.g., modify the original map by shoving new keys in, or trimming existing keys out, and the entire pointer bed is disavowed. That obviously includes outright destroying the original map. Buyer beware.
You are addressing 2 problems here:
Sorting a std::map or std::unordered_map according to its value
Using a kind of "compare callback" function for std::sort
Let us first tackle the point 2. If we read the description of std::sort in the CPP Reference here, then we see that can use a "comparison function object" as the 3rd parameter. This can be implemented with a Lambda, a functor or with adding a comparison operator to the object that will be sorted.
Let us assume we have a struct in a std::vector that we want to sort.
struct IntVal {
int val{};
};
std::vector<IntVal> intVals{};
If we want to sort this with a Lambda, then:
std::sort(intVals.begin(), intVals.end(), [](const IntVal& iv1, const IntVal& iv2) { return iv1.val < iv2.val; });
Or, you could add a comparison operator to your class/struct:
struct IntVal{
int val{};
bool operator < (const IntVal& other) const { return val < other.val; }
};
Or you can make your class a Functor, by adding a callable operator () to it.
#include <iostream>
#include <vector>
#include <algorithm>
struct IntVal{
int val{};
bool operator () (const IntVal& iv1, const IntVal& iv2) const { return iv1.val < iv2.val; }
};
std::vector<IntVal> intVals{ {3},{2},{1} };
int main() {
std::sort(intVals.begin(), intVals.end(), IntVal());
for (const IntVal& iv : intVals)
std::cout << iv.val << '\n';
}
Or, if you cannot modify the class, the create an external functor:
#include <iostream>
#include <vector>
#include <algorithm>
struct IntVal{
int val{};
};
// Functor
struct Comp {
bool operator () (const IntVal& iv1, const IntVal& iv2) const { return iv1.val < iv2.val; }
};
std::vector<IntVal> intVals{ {3},{2},{1} };
int main() {
std::sort(intVals.begin(), intVals.end(), Comp());
for (const IntVal& iv : intVals)
std::cout << iv.val << '\n';
}
Coming to the next topic.
You want to sort the data, stored in the std::unordered_map by its "value"-type.
This is not possible in place, because the nature of the maps is that they are already sorted. You cannot resort them. This would violate their internal behaviour.
The second problem is often that there key is unique. And sorting could also probaly destroy this strict requirement.
The solution is to use a second container. With you above mentioned "Sort" parameter. Copy the data to a second container and sort this.
We can use a std::vector and its range constructor to copy the data.
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <utility>
// Functor
struct Comp {
bool operator () (const std::pair<int,int>& p1, const std::pair<int, int>& p2) { return (p1.second == p2.second) ? p1.first<p2.first : p1.second>p2.second; }
};
int main() {
std::unordered_map<int, int> test{ {1,4},{2,3},{3,2},{4,1} };
std::vector<std::pair<int, int>> data(test.begin(), test.end());
std::sort(data.begin(), data.end(), Comp());
for (const auto[i1,i2] : data)
std::cout << i1 << ' ' << i2 << '\n';
}
Or, last but not least, and the final solution. We can use a 2nd container that will be sorted by definition, like a std::multiset
This will create a piece of code that is really easy to understand.
Please see:
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <utility>
#include <set>
// ------------------------------------------------------------
// Create aliases. Save typing work and make code more readable
using Pair = std::pair<int, int>;
// The map
using Map = std::unordered_map<Pair::first_type, Pair::second_type>;
// Sorted values will be stored in a multiset
struct Comp { bool operator ()(const Pair& p1, const Pair& p2) const { return (p1.second == p2.second) ? p1.first<p2.first : p1.second>p2.second; } };
using Sorter = std::multiset<Pair, Comp>;
int main() {
Map test{ {1,4},{2,3},{3,2},{4,1} };
Sorter sorter(test.begin(), test.end());
for (const auto[i1,i2] : sorter)
std::cout << i1 << ' ' << i2 << '\n';
}
And since what you really want to do is counting, please see an example for this as well. Here we will count as an example, the letters in a string:
#include <iostream>
#include <string>
#include <utility>
#include <set>
#include <unordered_map>
#include <type_traits>
#include <cctype>
// ------------------------------------------------------------
// Create aliases. Save typing work and make code more readable
using Pair = std::pair<char, unsigned int>;
// Standard approach for counter
using Counter = std::unordered_map<Pair::first_type, Pair::second_type>;
// Sorted values will be stored in a multiset
struct Comp { bool operator ()(const Pair& p1, const Pair& p2) const { return (p1.second == p2.second) ? p1.first<p2.first : p1.second>p2.second; } };
using Rank = std::multiset<Pair, Comp>;
// ------------------------------------------------------------
// --------------------------------------------------------------------------------------
// Compact function to calculate the frequency of charcters and then get their rank
Rank getRank(std::string& text) {
// Definition of our counter
Counter counter{};
// Iterate over all charcters in text and count their frequency
for (const char c : text) if (std::isalpha(c)) counter[char(std::tolower(c))]++;
// Return ranks,sorted by frequency and then sorted by character
return { counter.begin(), counter.end() };
}
// --------------------------------------------------------------------------------------
// Test, driver code
int main() {
// Get a string from the user
if (std::string text{}; std::getline(std::cin, text))
// Calculate rank and show result
for (const auto& [letter, count] : getRank(text))
std::cout << letter << " = " << count << '\n';
}

Range-based for loop with auto specifier combined with static_cast

Imaged that I have an std::vector of std::string, and I want to convert those std::string to std::string_view in range-based for loop:
auto v = std::vector<std::string>{"abc", "def", "ghi"};
for (std::string_view sv : v) {
// do something with string_view
}
The above code is perfectly valid, but I want to remain the auto specifier to do that, how to do static_cast in one line range-based for loop like this? It seems like C++20 ranges can do this in a concise way, can someone give an example?
for (auto sv : v | static_cast<std::string_view>) {
// do something with std::string_view
}
Not that this is a good idea as written, but this might be a useful example of a more general transform concept (and an evil lambda trick):
for(auto sv : v |
views::transform([](std::string_view x) {return x;})) …
You could, as #Elijay comments, simply create a new vector of string_views:
for (auto sv : vector<string_view>(v.begin(), v.end()))
but this kind of defeats the whole purpose of using string_view in the first place: to avoid copying.
It also, as commented below, kind of defeats the whole purpose of auto: to avoid unnecessarily restating type information. Here the type is being introduced for the first time so must be explicitly stated. Why not right up front in plain view?
Less verbose and using string_view as intended is where you started:
for (string_view sv : v)
You can do something like this:
#include <string>
#include <vector>
#include <iostream>
#include <string_view>
int main() {
auto v = std::vector<std::string>{"abc", "def", "ghi"};
for (auto sv : std::vector<std::string_view>(v.begin(), v.end())) {
// use sv ...
}
}
However note that creating a whole new vector is not at all recommended. It allocates memory again and causes a lot of unnecessary overhead. Besides, you have to spell the type somewhere anyway, so auto doesn't have any advantages at all here. The Right ThingTM to do is specifying the type name explicitly and not using auto.
Iterators is a good customization point, unfortunately it requires quite some boilerplate:
#include <vector>
#include <iostream>
#include <string_view>
template <typename T>
struct container_view {
const T& container;
container_view(const T& t) : container(t) {}
struct iterator{
typename T::const_iterator base;
iterator(const typename T::const_iterator& it) : base(it) {}
bool operator!=(const iterator& other) { return base != other.base; }
iterator& operator++() { ++base; return *this; }
std::string_view operator*() { return {*base}; }
// ^--- string_view
};
iterator begin() { return {container.begin()};}
iterator end() { return {container.end()};}
};
int main (){
auto v = std::vector<std::string>{"abc", "def", "ghi"};
// v--- auto
for (auto sv : container_view(v)) {
std::cout << sv << '\n';
}
}
Arguably more/less succinct. Opinions will vary.
#include <vector>
#include <string_view>
#include <string>
#include <iostream>
#include <range/v3/view/transform.hpp>
int main()
{
auto v = std::vector<std::string>{"abc", "def", "ghi"};
using namespace ranges;
auto as_string_view = views::transform([](auto&& x) { return std::string_view(x); });
for (auto sv : v | as_string_view) {
std::cout << sv << '\n';
}
}

Is it possible to initialize a vector from the keys in a map?

How to retrieve all keys (or values) from a std::map and put them into a vector? covers the ways to populate a std::vector from the keys in a map pre-C++11.
Is there a way to do this in C++11 using lambdas, etc, that means we can do it in one line so we can initialize the vector from the map instead of create a vector and populate it in 2 actions?
e.g. vector<int> v(???(m.begin(),m.end()));
Pure C++11 is preferred but boost is acceptable... the aim is to do this in one line without it being overly complicated and "showing off", so it's not confusing to other developers.
For comparison the "obvious" C++11 solution is:
vector<int> v;
v.reserve(m.size()); //not always needed
for(auto &x : map)
v.push_back(x.first)
Use boost::adaptor::map_keys in Boost.Range.
#include <iostream>
#include <vector>
#include <map>
#include <boost/range/adaptor/map.hpp>
int main()
{
const std::map<int, std::string> m = {
{1, "Alice"},
{2, "Bob"},
{3, "Carol"}
};
auto key_range = m | boost::adaptors::map_keys;
const std::vector<int> v(key_range.begin(), key_range.end());
for (int x : v) {
std::cout << x << std::endl;
}
}
Output:
1
2
3
There you go, C++11 one-liner :)
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <iterator>
int main(int, char**)
{
std::map<int, std::string> map {
{1, "one"},
{2, "two"},
{3, "three"}
};
std::vector<int> keys;
// Reserve enough space (constant-time)
keys.reserve(map.size());
// Retrieve the keys and store them into the vector
std::transform(map.begin(), map.end(), std::back_inserter(keys),
[](decltype(map)::value_type const &pair) {return pair.first;}
);// ^^^^^^^^^^^^^^^^^^^^^^^^^ Will benefit from C++14's auto lambdas
// Display the vector
std::copy(keys.begin(), keys.end(),
std::ostream_iterator<int>(std::cout, " "));
return 0;
}
std::transform is freaking powerful.
Something like the following would do:
#include <vector>
#include <map>
#include <boost/iterator/transform_iterator.hpp>
int main() {
std::map<std::string, int> m{{"abc", 1}, {"def", 2}};
auto extractor = [](decltype(m)::value_type const& kv) { return kv.first; };
std::vector<std::string> v(
boost::make_transform_iterator(m.begin(), extractor)
, boost::make_transform_iterator(m.end(), extractor)
);
}
Note that passing iterators to vector's constructor is the most efficient way to initialize a vector compared to solutions that use push_back or resize a vector filling it with default values first.
No, there's no way to do this in pure C++11 in one line using any of std::vectors constructor overloads, without e.g. creating your own iterator adaptor.
It's trivial to do in two lines though, e.g.:
std::vector<Key> v;
for (const auto& p : m) v.push_back(p.first);
It would also be easy to create your own iterator adaptor for this purpose, for example:
template <typename InputIt>
struct key_it : public InputIt {
key_it(InputIt it) : InputIt(it) {}
auto operator*() { return (*this)->first; }
};
// Helper function for automatic deduction of iterator type.
template <typename InputIt>
key_it<InputIt> key_adapt(InputIt it) {
return {it};
}
Now you can create and populate your std::vector in one line using:
std::vector<Key> v{key_adapt(std::begin(m)), key_adapt(std::end(m))};
Live example
A slight refinement of Quentin's solution:
std::vector<int> keys(map.size());
transform(map.begin(), map.end(), keys.begin(),
[](std::pair<int, std::string> const &p) { return p.first; });
or more readably:
std::vector<int> keys(map.size());
auto get_key = [](std::pair<int, std::string> const &p) { return p.first; };
transform(map.begin(), map.end(), keys.begin(), get_key);
probably better having:
int get_key(std::pair<int, std::string> const &p) { return p.first; }
std::vector<int> get_keys(const std::map<int, std::string> &map)
{
std::vector<int> keys(map.size());
transform(map.begin(), map.end(), keys.begin(), get_key);
return keys;
}
then calling:
std::vector<int> keys = get_keys(map);
if it's going to be used lots.
The std::vector class has two relevant constructors:
vector(std::initializer_list<T>) [C++11]
vector(InputIterator first, InputIterator last) [C++98]
The first is the new C++11 constructor that allows you to do things like:
std::vector<int> v{ 1, 2, 3 };
The second allows you to do things like:
std::vector<int> w{ v.rbegin(), v.rend() }; // 3, 2, 1
I don't see a way to use the initializer_list constructor (as you don't have the items available up-front), so your best bet would be to create a key_iterator class that works on a std::map<T, K>::iterator and returns (*i).first instead of (*i). For example:
std::vector<int> keys{ key_iterator(m.begin()), key_iterator(m.end()) };
This also requires you to write the key_iterator class, which you can use the Boost iterator adapters to simplify the task. It may be easier just to use the 2 line version.
Whoopse, I realize this does not actually answer your question (must learn to read properly)!
I would probably go with something like this:
#include <map>
#include <vector>
#include <iostream>
int main()
{
std::map<int, std::string> m =
{
{1, "A"}, {2, "B"}, {3, "C"}
};
std::vector<int> v;
v.reserve(m.size());
for(auto&& i: m)
v.emplace_back(i.first);
for(auto&& i: v)
std::cout << i << '\n';
}

How to combine a function and a predicate in for_each?

How can you call a Function over some part of a container, using for_each() ?
I have created a for_each_if() to do a
for( i in shapes )
if( i.color == 1 )
displayShape(i);
and the call looks like
for_each_if( shapes.begin(), shapes.end(),
bind2nd( ptr_fun(colorEquals), 0 ),
ptr_fun( displayShape ) );
bool colorEquals( Shape& s, int color ) {
return s.color == color;
}
However, I feel immitating STL-like algorithms is not something that I should be doing.
Is there a way to use only existing STL keywords to produce this ?
I did not want to do a
for_each( shapes.begin(), shapes.end(),
bind2nd( ptr_fun(display_shape_if_color_equals), 0 ) );
because, in a more complicated case, the functor name would be misleading with respect to what the functor
*Is there a way to access a struct's member (like colorEquals) for functions like for_each without having to create a function ? *
Imitating STL-like algorithms is exactly what you should be doing. That's why they're in the STL.
Specifically, you can use a functor instead of creating an actual function and binding it. This is much neater, really.
template<typename Iterator, typename Pred, typename Operation> void
for_each_if(Iterator begin, Iterator end, Pred p, Operation op) {
for(; begin != end; begin++) {
if (p(*begin)) {
op(*begin);
}
}
}
struct colorequals {
colorequals(int newcol) : color(newcol) {}
int color;
bool operator()(Shape& s) { return s.color == color; }
};
struct displayshape {
void operator()(Shape& s) { // display the shape }
};
for_each_if(shapes.begin(), shapes.end(), colorequals(0), displayshape());
This is usually considered the idiomatic way to go.
Using boost range adaptors is much neater.
using boost::adaptor::filtered;
using boost::bind;
class Shape {
int color() const;
};
void displayShape(const Shape & c);
bool test_color(const Shape & s, int color ){
return s.color() == color;
}
boost::for_each
( vec | filtered(bind(&test_color, _1, 1)
, bind(&displayShape, _1)
)
Note the use of the new range library to abstract away
iterators in favor of ranges and the range adaptors
library to compose a pipeline of operations.
All the standard stl iterator based algorithms have
been ported to range based algorithms.
Imagine this
typedef boost::unordered_map<int, std::string> Map;
Map map;
...
using boost::adaptor::map_keys;
using boost::bind
using boost::ref
using boost::adaptor::filtered;
bool gt(int a, int b)
{ return a > b };
std::string const & get(const Map & map, int const & a)
{ return map[a] }
// print all items from map whose key > 5
BOOST_FOREACH
( std::string const & s
, map
| map_keys
| filtered(bind(&gt, _1, 5))
| transformed(bind(&get, ref(map), _1))
)
{
cout << s;
}
Read Range Adaptors and Range Algorithm.
You can use the C++20 ranges. Here an example where we add one to all even numbers of a std::vector
#include <ranges>
#include <algorithm>
#include <vector>
namespace ranges = std::ranges;
std::vector<int> vec = {1, 2, 3, 4, 5};
const auto even = [](int i) { return 0 == i % 2; };
ranges::for_each(vec | std::views::filter(even), [](int& i){ i+=1;});
You can find a living example on compiler explorer here
To use a regular for_each with an if you would need a Functor that emulates an if condition.
#include <algorithm>
#include <vector>
#include <functional>
#include <iostream>
#include <boost/bind.hpp>
using namespace std;
struct incr {
typedef void result_type;
void operator()(int& i) { ++i; }
};
struct is_odd {
typedef bool return_type;
bool operator() (const int& value) {return (value%2)==1; }
};
template<class Fun, class Cond>
struct if_fun {
typedef void result_type;
void operator()(Fun fun, Cond cond, int& i) {
if(cond(i)) fun(i);
}
};
int main() {
vector<int> vec;
for(int i = 0; i < 10; ++i) vec.push_back(i);
for_each(vec.begin(), vec.end(), boost::bind(if_fun<incr, is_odd>(), incr(), is_odd(), _1));
for(vector<int>::const_iterator it = vec.begin(); it != vec.end(); ++it)
cout << *it << " ";
}
Unfortunately my template hackery isn't good enough to manage this with bind1st and bind2nd as it somehow gets confusing with the binder being returned being a unary_function but it looks pretty good with boost::bind anyhow. My example is no means perfect as it doesn't allow the Func passed into if_fun to return and I guess somebody could point out more flaws. Suggestions are welcome.

Initializing a static std::map<int, int> in C++

What is the right way of initializing a static map? Do we need a static function that will initialize it?
Using C++11:
#include <map>
using namespace std;
map<int, char> m = {{1, 'a'}, {3, 'b'}, {5, 'c'}, {7, 'd'}};
Using Boost.Assign:
#include <map>
#include "boost/assign.hpp"
using namespace std;
using namespace boost::assign;
map<int, char> m = map_list_of (1, 'a') (3, 'b') (5, 'c') (7, 'd');
Best way is to use a function:
#include <map>
using namespace std;
map<int,int> create_map()
{
map<int,int> m;
m[1] = 2;
m[3] = 4;
m[5] = 6;
return m;
}
map<int,int> m = create_map();
It's not a complicated issue to make something similar to boost. Here's a class with just three functions, including the constructor, to replicate what boost did (almost).
template <typename T, typename U>
class create_map
{
private:
std::map<T, U> m_map;
public:
create_map(const T& key, const U& val)
{
m_map[key] = val;
}
create_map<T, U>& operator()(const T& key, const U& val)
{
m_map[key] = val;
return *this;
}
operator std::map<T, U>()
{
return m_map;
}
};
Usage:
std::map mymap = create_map<int, int >(1,2)(3,4)(5,6);
The above code works best for initialization of global variables or static members of a class which needs to be initialized and you have no idea when it gets used first but you want to assure that the values are available in it.
If say, you've got to insert elements into an existing std::map... here's another class for you.
template <typename MapType>
class map_add_values {
private:
MapType mMap;
public:
typedef typename MapType::key_type KeyType;
typedef typename MapType::mapped_type MappedType;
map_add_values(const KeyType& key, const MappedType& val)
{
mMap[key] = val;
}
map_add_values& operator()(const KeyType& key, const MappedType& val) {
mMap[key] = val;
return *this;
}
void to (MapType& map) {
map.insert(mMap.begin(), mMap.end());
}
};
Usage:
typedef std::map<int, int> Int2IntMap;
Int2IntMap testMap;
map_add_values<Int2IntMap>(1,2)(3,4)(5,6).to(testMap);
See it in action with GCC 4.7.2 here: http://ideone.com/3uYJiH
############### EVERYTHING BELOW THIS IS OBSOLETE #################
EDIT: The map_add_values class below, which was the original solution I had suggested, would fail when it comes to GCC 4.5+. Please look at the code above for how to add values to existing map.
template<typename T, typename U>
class map_add_values
{
private:
std::map<T,U>& m_map;
public:
map_add_values(std::map<T, U>& _map):m_map(_map){}
map_add_values& operator()(const T& _key, const U& _val)
{
m_map[key] = val;
return *this;
}
};
Usage:
std::map<int, int> my_map;
// Later somewhere along the code
map_add_values<int,int>(my_map)(1,2)(3,4)(5,6);
NOTE: Previously I used a operator [] for adding the actual values. This is not possible as commented by dalle.
##################### END OF OBSOLETE SECTION #####################
Here is another way that uses the 2-element data constructor. No functions are needed to initialize it. There is no 3rd party code (Boost), no static functions or objects, no tricks, just simple C++:
#include <map>
#include <string>
typedef std::map<std::string, int> MyMap;
const MyMap::value_type rawData[] = {
MyMap::value_type("hello", 42),
MyMap::value_type("world", 88),
};
const int numElems = sizeof rawData / sizeof rawData[0];
MyMap myMap(rawData, rawData + numElems);
Since I wrote this answer C++11 is out. You can now directly initialize STL containers using the new initializer list feature:
const MyMap myMap = { {"hello", 42}, {"world", 88} };
For example:
const std::map<LogLevel, const char*> g_log_levels_dsc =
{
{ LogLevel::Disabled, "[---]" },
{ LogLevel::Info, "[inf]" },
{ LogLevel::Warning, "[wrn]" },
{ LogLevel::Error, "[err]" },
{ LogLevel::Debug, "[dbg]" }
};
If map is a data member of a class, you can initialize it directly in header by the following way (since C++17):
// Example
template<>
class StringConverter<CacheMode> final
{
public:
static auto convert(CacheMode mode) -> const std::string&
{
// validate...
return s_modes.at(mode);
}
private:
static inline const std::map<CacheMode, std::string> s_modes =
{
{ CacheMode::All, "All" },
{ CacheMode::Selective, "Selective" },
{ CacheMode::None, "None" }
// etc
};
};
I would wrap the map inside a static object, and put the map initialisation code in the constructor of this object, this way you are sure the map is created before the initialisation code is executed.
Just wanted to share a pure C++ 98 work around:
#include <map>
std::map<std::string, std::string> aka;
struct akaInit
{
akaInit()
{
aka[ "George" ] = "John";
aka[ "Joe" ] = "Al";
aka[ "Phil" ] = "Sue";
aka[ "Smitty" ] = "Yando";
}
} AkaInit;
You can try:
std::map <int, int> mymap =
{
std::pair <int, int> (1, 1),
std::pair <int, int> (2, 2),
std::pair <int, int> (2, 2)
};
If you are stuck with C++98 and don't want to use boost, here there is the solution I use when I need to initialize a static map:
typedef std::pair< int, char > elemPair_t;
elemPair_t elemPairs[] =
{
elemPair_t( 1, 'a'),
elemPair_t( 3, 'b' ),
elemPair_t( 5, 'c' ),
elemPair_t( 7, 'd' )
};
const std::map< int, char > myMap( &elemPairs[ 0 ], &elemPairs[ sizeof( elemPairs ) / sizeof( elemPairs[ 0 ] ) ] );
This is similar to PierreBdR, without copying the map.
#include <map>
using namespace std;
bool create_map(map<int,int> &m)
{
m[1] = 2;
m[3] = 4;
m[5] = 6;
return true;
}
static map<int,int> m;
static bool _dummy = create_map (m);
In addition to the good top answer of using
const std::map<int, int> m = {{1,1},{4,2},{9,3},{16,4},{32,9}}
there's an additional possibility by directly calling a lambda that can be useful in a few cases:
const std::map<int, int> m = []()->auto {
std::map<int, int> m;
m[1]=1;
m[4]=2;
m[9]=3;
m[16]=4;
m[32]=9;
return m;
}();
Clearly a simple initializer list is better when writing this from scratch with literal values, but it does open up additional possibilities:
const std::map<int, int> m = []()->auto {
std::map<int, int> m;
for(int i=1;i<5;++i) m[i*i]=i;
m[32]=9;
return m;
}();
(Obviously it should be a normal function if you want to re-use it; and this does require recent C++.)
You have some very good answers here, but I'm to me, it looks like a case of "when all you know is a hammer"...
The simplest answer of to why there is no standard way to initialise a static map, is there is no good reason to ever use a static map...
A map is a structure designed for fast lookup, of an unknown set of elements. If you know the elements before hand, simply use a C-array. Enter the values in a sorted manner, or run sort on them, if you can't do this. You can then get log(n) performance by using the stl::functions to loop-up entries, lower_bound/upper_bound. When I have tested this previously they normally perform at least 4 times faster than a map.
The advantages are many fold...
- faster performance (*4, I've measured on many CPU's types, it's always around 4)
- simpler debugging. It's just easier to see what's going on with a linear layout.
- Trivial implementations of copy operations, should that become necessary.
- It allocates no memory at run time, so will never throw an exception.
- It's a standard interface, and so is very easy to share across, DLL's, or languages, etc.
I could go on, but if you want more, why not look at Stroustrup's many blogs on the subject.