STL map "[]" operator can insert new entries or modify existing entries.
map<string, string> myMap;
myMap["key1"] = "value1";
myMap["key1"] = "value2";
I am rewriting some code with boost::bimap which was implemented by STL map. Is there an easy way to keep the STL "[]" behavior? I found I have to write below 7 lines code to replace the original STL map code (1 line!).
bimap<string, string>::left_iterator itr = myBimap.left.find("key1");
if (itr != myBimap.left.end()) {
myBimap.left.replace_data(itr, "value2");
}
else {
myBimap.insert(bimap<string, string>::value_type("key1", "value2"));
}
I was wondering if there's an utility function like boost::bimap::insert_or_modify().
The Boost.Bimap documentation shows how mimic a std::map including its operator[] by using set_of and list_of for the bimap template arguments:
#include <iostream>
#include <string>
#include <boost/bimap.hpp>
#include <boost/bimap/set_of.hpp>
#include <boost/bimap/list_of.hpp>
int main()
{
using namespace std;
map<string, string> myMap;
myMap["key1"] = "value1";
myMap["key1"] = "value2";
for (auto&& elem : myMap)
std::cout << "{" << elem.first << ", " << elem.second << "}, ";
std::cout << "\n";
using namespace boost::bimaps;
bimap<set_of<string>, list_of<string>> myMap2;
myMap2.left["key1"] = "value1";
myMap2.left["key1"] = "value2";
for (auto&& elem : myMap2.left)
std::cout << "{" << elem.first << ", " << elem.second << "}, ";
std::cout << "\n";
auto res1 = myMap2.left.find("key1");
std::cout << "{" << res1->first << ", " << res1->second << "} \n";
}
Live Example.
UPDATE: the above code also allows left-searches. However, a right-search is not possible in combination with the required operator[] syntax. The reason is that operator[] modifications can only be done with a mutable right-view (such list_of or vector_of). OTOH, right-searches can only be done from immutable set_of and unordered_set_of and their multi- cousins.
Related
#include <iostream>
#include <any>
#include <vector>
#include <map>
#include <unordered_map>
#include <string>
using namespace std;
// enable_if_t = MapType implements .find(KeyType key), .begin(), .end(), and .begin()
// and MapType::iterator it has it->first and it->second
template<typename MapType>
decltype(declval<MapType>().begin()->second) MapGetOrDefault(
MapType mymap,
decltype(declval<MapType>().begin()->first) key,
decltype(declval<MapType>().begin()->second) defaultValue = decltype(declval<MapType>().begin()->second){}
)
{
auto it = mymap.find(key);
if (it!=mymap.end()) return it->second;
else return defaultValue;
}
int main()
{
map<string, int> a;
unordered_map<int, string> b;
a["1"] = 2;
cout << MapGetOrDefault(a, "1") << " " << MapGetOrDefault(a, "2") << " " << MapGetOrDefault(a, "2", -1) << "\n";
b[1] = "hello";
cout << MapGetOrDefault(b, 1) << " " << MapGetOrDefault(b, 2) << " " << MapGetOrDefault(b, 3, "world") << "\n";
return 0;
}
I'm trying to make a generic get_or_default() function that can be used with all types of map. There are 4 conditions that a class must meet to be counted as map, like shown in the code comment.
How can I do this using C++ type traits? Also, how to change decltype(declval<MapType>().begin()->first) into something more clean?
Edit: is enable_if_t even needed in this case? My goal is to just prevent compilation
Maps have member aliases mapped_type and key_type (and value_type) that you can use :
#include <iostream>
#include <any>
#include <vector>
#include <map>
#include <unordered_map>
#include <string>
using namespace std;
template<typename MapType>
typename MapType::mapped_type MapGetOrDefault(
MapType mymap,
typename MapType::key_type key,
typename MapType::mapped_type defaultValue = typename MapType::mapped_type{}
)
{
auto it = mymap.find(key);
if (it!=mymap.end()) return it->second;
else return defaultValue;
}
int main()
{
map<string, int> a;
unordered_map<int, string> b;
a["1"] = 2;
cout << MapGetOrDefault(a, "1") << " " << MapGetOrDefault(a, "2") << " " << MapGetOrDefault(a, "2", -1) << "\n";
b[1] = "hello";
cout << MapGetOrDefault(b, 1) << " " << MapGetOrDefault(b, 2) << " " << MapGetOrDefault(b, 3, "world") << "\n";
return 0;
}
Template aliases can help for more terse syntax:
template <typename T> using mapped_type = typename T::mapped_type;
template <typename T> using key_type = typename T::key_type;
template<typename MapType>
mapped_type<MapType> MapGetOrDefault(
MapType mymap,
key_type<MapType> key,
mapped_type<MapType> defaultValue = mapped_type<MapType>{}
)
{
auto it = mymap.find(key);
if (it!=mymap.end()) return it->second;
else return defaultValue;
}
PS: I didnt change it because it wasn't part of the question, but you should not make a copy of the map when passing it to the function, rather use const MapType& mymap for the argument.
PPS: As mentioned by Caleth in a comment, with a transparent comparator, ie one that has a Comparator::is_transparent member type, you might want to support the overload (4) listed here: https://en.cppreference.com/w/cpp/container/map/find by introducing another template argument KeyType that can be deduced from the key paramter.
This is a follow up question from Cout from a map with std::tuple
I have made a small map that I call BMW. It contains the keys Usage and Diesel, as shown below.
#include <iostream>
#include <bits/stdc++.h>
#include <map>
#include <vector>
using namespace std;
int main()
{
// initialize container
map<string, tuple<string, string>> BMW;
// insert elements
BMW.insert({"Usage", {"1", "2"}});
BMW.insert({"Disel", {"2", "3"}});
string sFirst_value;
string sSecond_value;
//prints out the map
for (const auto& x : BMW) {
sFirst_value.assign(get<0>(BMW.find(x.first)->second));
sSecond_value.assign(get<1>(BMW.find(x.first)->second));
cout << x.first << "\n" << "Min: " << sFirst_value << "\n" << "Max: " << sSecond_value << "\n" << "\n";
}
return 0;
}
Is there anyway I can call the name of the map, BMW, from a string instead of writing BMW.insert({"Usage", {"1", "2"}});? Like this:
stirng Mycar;
Mycar.insert({"Usage", {"1", "2"}});
To expand on Quentin's comment with a small example:
std::map<std::string, std::map<std::string, std::tuple<std::string, std::string>>> mapMap;
std::string myCar = "BMW";
std::map<std::string, std::tuple<std::string, std::string>> &myCarMap = mapMap[myCar];
myCarMap.insert({"Usage", {"1", "2"}});
//Or simply
auto &bmwMap = mapMap["BMW"];
bmwMap.insert({"Usage", {"1", "2"}});
}
Probably you can find better names than mapMap though ;)
I have done the programming but it is not reversing. I have used a different map to put the values in reverse order,but it still shows the same. My main question was to traverse backward and print the values using range based loop.
#include "stdafx.h"
#include <iostream>
#include<conio.h>
#include <stdio.h>
#include<vector>
#include<map>
#include<utility>
#include<set>
map<int, int>m1;
for (int i = 1; i <= 100; ++i)
{
m1.insert({ i,i });
}
for (const auto &y :m1)
{
cout <<"("<< y.first << " "<<y.second << ")" <<" " ;
}
cout << endl << endl;
map<int, int>m2;
map<int, int>::reverse_iterator iter;
for (auto iter = m1.rbegin(); iter != m1.rend(); ++iter)
{
m2.insert({ iter->first,iter->second });
}
for (const auto &y : m2)
{
cout << "(" << y.first << " " << y.second << ")" << " ";
}
As Some Programmer Dude pointed out, but for the completeness of my answer, a std::map is sorted on the key, no matter what order you insert the elements. One option would be to create a new map with the opposite sorting, but that doesn't seem to be what you really want.
It seems you know how about reverse iterators, but not how to get at them when using range-based for. Since it operates on a range, i.e. some type that provides begin and end iterators, you need to create some wrapper around your map that provides this.
Here's a general one I just put together than works in C++11. It won't cover every possible case, and can be made a bit neater in C++14, but it will work for you.
#include <iostream>
#include <iterator>
// The wrapper type that does reversal
template <typename Range>
class Reverser {
Range& r_;
public:
using iterator_type = std::reverse_iterator<decltype(std::begin(r_))>;
Reverser(Range& r) : r_(r) {}
iterator_type begin() { return iterator_type(std::end(r_)); }
iterator_type end() { return iterator_type(std::begin(r_)); }
};
// Helper creation function
template <typename Range>
Reverser<Range> reverse(Range& r)
{
return Reverser<Range>(r);
}
int main()
{
int vals[] = {1, 2, 3, 4, 5};
for (auto i : reverse(vals))
std::cout << i << '\n';
}
This outputs:
$ ./reverse
5
4
3
2
1
(You may also find libraries that provide a similar adapter; Eric Niebler is working on a ranges library for The Standard.)
Also, please reconsider your use of what are often considered bad practices: using namespace std; and endl (those are links to explanations).
Here's an example of iterating backward through a std::map:
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<int, int> m;
m[1] = 1;
m[2] = 2;
m[3] = 3;
for (auto iter = m.rbegin(); iter != m.rend(); ++iter) {
std::cout << iter->first << ": " << iter->second << std::endl;
}
}
If you are pre-C++11, you'll just need to spell out auto, which is:
std::map<int, int>::reverse_iterator
If you're using boost, you can use a range-based for loop with a reverse adapter:
#include <boost/range/adaptor/reversed.hpp>
for (auto& iter : boost::adaptors::reverse(m)) {
std::cout << iter.first << ": " << iter.second << std::endl;
}
If you only need to print the elements in the map in reverse order,you don't need another map for it,you can do this:
std::map<int, int>::reverse_iterator iter;
for (iter = m1.rbegin(); iter != m1.rend(); ++iter)
{
std::cout << "(" << iter->first << " " << iter->second << ")" << " ";
}
I have an STL map with a custom comparator which I want to pass to a function, but the function doesn't recognize the custom comparator.
Trying to access the map within the main function works.
I have listed both attempts in my code.
#include <iostream>
#include <string>
#include <map>
// Error: cmpByStringLength is not recognized (both times)
void funcOut(std::map<std::string, int, cmpByStringLength> myMap)
{
for (std::map<std::string, int, cmpByStringLength>::iterator it = myMap.begin(); it != myMap.end(); ++it)
{
std::cout << it->first << " => " << it->second << std::endl;
}
}
int main()
{
// Reverse sort by length
struct cmpByStringLength {
bool operator()(const std::string& a, const std::string& b) const {
return a.length() > b.length();
}
};
std::map<std::string, int, cmpByStringLength> myMap;
myMap.emplace("String 1", 5);
myMap.emplace("String 123", 10);
funcOut(myMap);
// Working
for (std::map<std::string, int, cmpByStringLength>::iterator it = myMap.begin(); it != myMap.end(); ++it)
{
std::cout << it->first << " => " << it->second << std::endl;
}
return 0;
}
You can only use a name after its declaration, and only if it's in scope. Your comparator type is scoped within main, so you can only use it in that function. Move the definition out of main, into the global namespace (or in another namespace if you like), to make it available in other functions.
Alternatively, you could make the other function a template, so it can work with any map type:
template <typename Map>
void funcOut(Map const & myMap) {
// your code here
}
Use a template, because I'm a lazy c++ developer (I don't need to worry about lots of details...) I would do..
template <typename MapType>
void funcOut(MapType& myMap)
{
for (auto& p : myMap)
{
std::cout << p.first << " => " << p.second << std::endl;
}
}
I use about 6 different C++ containers. I started writing print functions to output the container contents. Is this necessary? I would think this is part of the C++ library?
void print_list(const list<int>& list_int)
{
for (list<int>::const_iterator it = list_int.begin(); it != list_int.end(); it++) cout << *it << " ";
}
void print_um(const unordered_map<int, double>& map_int_d)
{
for(unordered_map<int, double>::const_iterator it = map_int_d.begin(); it != map_int_d.end(); ++it)cout << "[" << it->first << "," << it->second << "] ";
}
It's not part of the library, but its easy to write with the tools provided:
C c; // Where C is a container type
std::copy(
c.begin(), c.end()
, std::ostream_iterator< C::value_type >( cout, " " )
);
For containers whose element is a pair (like map and I'd believe unordered_map) you'd need a custom Output Iterator to print the pair with the comma and brackets.
The code you give in your question has a hint as to why it is not part of the standard library. Your code uses square brackets and a comma with no spaces to show the pairs in the map. Other people may want it formatted differently so the options the standards committee had were:
Provide a lot of formatting options.
Provide no formatting options and make everyone who doesn't like their formatting roll their own.
Do nothing and make everyone roll their own
They went with option three knowing that libraries would be developed that meet people's specific needs.
What about:
#include <iostream>
#include <map>
#include <algorithm>
template <typename K, typename V>
std::ostream& operator<<(std::ostream& os, const std::pair<K,V>& p)
{
return os << "[" << p.first << ", " << p.second << "]";
}
template <typename Container>
std::ostream& operator<<(std::ostream& os, const Container& c)
{
std::copy(c.begin(), c.end(),
std::ostream_iterator<typename Container::value_type>(os, " "));
return os;
}
You might also be charmed about Boost Spirit Karma:
#include <boost/spirit/include/karma.hpp>
using boost::spirit::karma::format;
using boost::spirit::karma::auto_;
int main()
{
std::vector<int> ints(1000);
std::map<std::string, int> pairs();
// ...
std::cout << format(auto_ % " ", ints) << std::endl;
std::cout << format(('[' << auto_ << ',' << ']') % " ", pairs) << std::endl;
}