The essence of the program: get a map, where the values are char from the passed string, and the key is the number of these values in the string
using namespace std;
map<char, int> is_merge(const string& s) {
map<char, int> sCount {};
for (auto lp : s) {
if (find_if(sCount.begin(), sCount.end(), lp) != sCount.end()) {
sCount[lp] += 1;
} else {
sCount.insert(make_pair(lp, 0));
}
}
return sCount;
}
int main()
{
string test = "aba";
map <char, int> res = is_merge(test);
for (auto lp : res) {
cout << lp.first << ":" << lp.second << endl;
}
return 0;
}
But an error occurs in the console: /usr/include/c++/12/bits/predefined_ops.h:318:30: error: expression cannot be used as a function 318 | { return bool(_M_pred(*__it)); } | ~~~~~~~^~~~~~~
std::find_if takes a predicate not a value. Hence the error that lp is not a callable. To find a key in a map you should use std::map::find because it is O(logn) compared to O(n) for std::find/std::find_if (as a rule of thumb you can remember: If a container has a member function that does the same as a generic algorithm the member function is at least as effcient, often better).
However, there is not need to check if the key is present via find. The function can be this:
map<char, int> is_merge(const string& s) {
map<char, int> sCount {};
for (auto lp : s) {
++sCount[lp];
}
return sCount;
}
std::map::operator[] already does insert an element when none is found for the given key. You don't need to do that yourself.
PS: And if you do call insert then there is no need for std::make_pair : sCount.insert({lp, 0});. std::make_pair is for when you need to deduce the type of the pair from arguments to std::make_pair, but you don't need that here.
PPS: And if you do use std::find you need to consider that the element type of your map is std::pair<const char, int>, not char.
Related
#include <iostream>
#include <map>
int main(void) {
std::map<char, int> mapint;
mapint.insert({'a', 1});
mapint.insert({'b', 2});
// subscript operator is overloaded to return iterator.second (the value with key 'a')
int ex = mapint['a'];
std::cout << ex << std::endl;
// Why does this NOT traslate to 1=10 ?
// instead it replaces or creates pair <'a',10>...
mapint['a'] = 10;
for (auto i : mapint) {
std::cout << i.first << "," << i.second << std::endl;
}
// OUTPUT
// 1
// a,10
// b,2
return 0;
}
How is the map operator being overloaded? I tried looking at the code for map but i couldn't find anything to answer my question...
I want to make something similar for one of my classes and i think figuring this out should help alot!
It basically just builds on top of existing methods found within map. It is implemented along the lines of...
template<typename Key, typename Value>
struct map {
// This operator cannot be declared 'const', since if the key
// is not found, a new items is automatically added.
Value& operator [] (const Key& key) {
// attempt to find the key
auto iter = find(key);
// if not found...
if(iter == end()) {
// insert new item (with a default value to start with)
iter = insert(std::make_pair(key, Value()));
}
// return reference to the data item stored for 'key'
return iter->second;
}
};
To overload the [] operator you can do as follows (in pseudo-code):
struct A
{
your_return_type operator[](your_argument_list)
{
your implementation
}
};
If you want to return a reference to some class member, then you may have to implement 2 versions of this operator, one const, which returns a non-modifiable reference, and one non-const, which returns a modifiable refence.
struct A
{
your_modifiable_return_type_ref& operator[](your_argument_list)
{
your implementation
}
const your_non_modifiable_return_type_ref& operator[](your_argument_list) const
{
your implementation
}
};
How to sort the multimap using comparator.
It should be reflected in the multimap container
#include <bits/stdc++.h>
using namespace std;
// Comparator function to sort pairs
// according to second value
bool cmp(pair<string, int>& a,
pair<string, int>& b)
{
return a.second < b.second;
}
// Function to sort the map according
// to value in a (key-value) pairs
void sort(multimap<string, int>& M)
{
// Declare vector of pairs
vector<pair<string, int> > A;
// Copy key-value pair from Map
// to vector of pairs
for (auto& it : M) {
A.push_back(it);
}
// Sort using comparator function
sort(A.begin(), A.end(), cmp);
// Print the sorted value
for (auto& it : A) {
cout << it.first << ' '
<< it.second << endl;
}
}
// Driver Code
int main()
{
// Declare Map
multimap<string, int> M;
// Given Map
M = { { "GfG", 3 },
{ "To", 2 },
{ "Welcome", 1 } };
// Function Call
sort(M);
cout<<"\n";
for(auto i:M)
{
cout<<i.first<<" "<<i.second<<endl;
}
return 0;
}
This is code I got from Geekforgeeks!
I totally understood the concept but I need to know how to sort the map itself if the map is multimap.?
OUTPUT
Welcome 1
To 2
GfG 3
GfG 3
To 2
Welcome 1
Basically the answer has been given in the comments already. I will summarize again and show an alternative.
The background is that we often want to use the properties of an associative container, but later sort it by its value and not by the key.
The unsorted associative containers, like std::unsorted_set, std::unordered_map , std::unordered_multiset and std::unordered_multimap cannot be ordered, as their names say. They are using a hash algorithm to store and retrieve their values.
Please read also in the CPP Reference about STL container.
And, if we look at the sorted associative container, we see that the std::set and the std::multiset do not work with key value pairs and the other two, the std::map and std::multimap have a key and value but are always sorted by their key.
But as said, we often want to have the advantages of an Associative container with a key - value pair and later some sorted-by-the-value data.
This can only be acieved by using 2 container having the needed property. In your above example the data is copied into a std::vector and then sorted. This mechanism can be used always, with any sortable container, by adding a std::pair of key and value to a container. So:
using Pair = std::pair<std::string, int>;
using Conatiner = std::vector<Pair>;
An often needed use case is counting something. For example, counting the letters in a string. This can be really easily achieved with a std::map or std::unordered_map and the sorted by the value using a 2nd container. Please see some example code below:
#include <iostream>
#include <string>
#include <utility>
#include <set>
#include <unordered_map>
#include <type_traits>
// ------------------------------------------------------------
// 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, because their may be double ranks
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>;
// ------------------------------------------------------------
// Function to get the rank of letters in a string
Rank getRank(const std::string& str) {
Counter counter;
for (const char c : str) counter[c]++;
return { counter.begin(), counter.end() };
}
// Test / Driver Code
int main() {
if (std::string userString{}; std::getline(std::cin, userString))
for (const auto& [letter, count] : getRank(userString))
std::cout << (int)letter << ' ' << count << ' ';
}
So, we use the property of the std::unordred map as a fast associative container, not sorted by the key, in combination with a std::multiset which will act as a "sorter".
Or, you can take a std::multiset from the beginning. Example:
#include <iostream>
#include <string>
#include <utility>
#include <set>
// ------------------------------------------------------------
// Create aliases. Save typing work and make code more readable
using Pair = std::pair <std::string, int> ;
// 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 Sorted = std::multiset<Pair, Comp>;
// ------------------------------------------------------------
// Driver Code
int main()
{
Sorted data { { "GfG", 3 }, { "To", 2 }, { "Welcome", 1 } };
for (const auto& [key, value] : data)
std::cout << key << '\t' << value << '\n';
}
Depends of course all on what you want to achieve . . .
I am trying to create an unordered_map for <xml_node*,string> pair, where xml_node is an element of xml from pugixml library, and i wish to store its pointer as the key. I have declared the map like this :
unordered_map<xml_node*,string> label_hash;
Now the insert function is working well and good. But whenever I try to find an element from the hash like this :
string lb = string(label_hash.find(node));
I get the following error :
no matching function for call to ‘std::basic_string<char>::basic_string(std::_Hashtable<pugi::xml_node*, std::pair<pugi::xml_node* const, std::basic_string<char> >, std::allocator<std::pair<pugi::xml_node* const, std::basic_string<char> > >, std::_Select1st<std::pair<pugi::xml_node* const, std::basic_string<char> > >, std::equal_to<pugi::xml_node*>, std::hash<pugi::xml_node*>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, false, false, true>::iterator)’|
Now do I need to implement a hash function and equal function for the map? I was trying to implement them like follows but it doesn't work :
struct hashing_func {
unsigned long operator()(const xml_node* key) const {
uintptr_t ad = (uintptr_t)key;
return (size_t)((13 * ad) ^ (ad >> 15));
//return hash<xml_node*>(key);
}
};
struct key_equal_fn {
bool operator()(const xml_node* t1, const xml_node* t2) const {
return (t1 == t2);
}
};
I am a bit new to C++ so a little help would be great!
Please read the documentation: unordered_map::find returns an iterator to pair<xml_node const*, string>. (You can't pass that to the string constructor.) Instead do this:
auto iterator = label_hash.find(node);
if (iterator != label_hash.end()) { // `.find()` returns `.end()` if the key is not in the map
string& lb = iterator->second; // The `&` is optional here, use it if you don't want to deepcopy the whole string.
// use lb
}
else {
// key not in the map
}
I wrote a little test program:
#include <unordered_map>
#include <string>
namespace pugi
{
struct xml_node {};
}
int main()
{
std::unordered_map<pugi::xml_node*, std::string> mymap;
pugi::xml_node n1;
mymap.emplace(&n1, "foo");
auto i = mymap.find(&n1);
i->second;
return 0;
}
This compiles perfectly, indicating that as, suspected, the problem is not with the use of a pointer as a map key, not a lack of custom comparitor and not the lack of a hash function.
unordered_map::find returns an iterator - which points to a key/value pair.
struct MapInserter
{
private:
int count;
public:
explicit MapInserter()
: count(0)
{
}
std::pair<int, std::string> operator()(std::string& value)
{
return std::make_pair(count++, value);
}
};
vector<std::string> words = { "one", "two", "three","four","five" };
std::map<int, std::string> map;
MapInserter inserter;
transform(words.begin(), words.end(), map.begin(), inserter);
for (auto it = map.begin(), end = map.end(); it != end; ++it)
cout << it->first << " : " << it->second << endl;
return 0;
that's the code. VS returns a compile error regarding l-value specifies const object.
Clicking the error moves you to the following code in a file named utility
template<class _Other1,
class _Other2>
_Myt& operator=(pair<_Other1, _Other2>&& _Right)
{ // assign from moved compatible pair
first = _STD forward<_Other1>(_Right.first);
second = _STD forward<_Other2>(_Right.second);
return (*this);
}
at first, i've had the operator() take const std::string& so I removed the const, because it's obviously talking about the make_pair function. But it still hasn't gone away. Can anyone point me to what this error is about?
The problem is that std::transform() will try to assign to existing elements of the target container. Keys of a map are constant and cannot be assigned to, which is why you're getting a compiler error. But even if they were, you'd get undefined behavior at run-time here, because the target container is empty, and std::transform() would expect it to contain as many elements as the input range.
You should use std::inserter() to create an inserter iterator, like so:
vector<std::string> words = { "one", "two", "three","four","five" };
std::map<int, std::string> map;
MapInserter inserter;
transform(words.begin(), words.end(), std::inserter(map, map.begin()), inserter);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Here is a live example.
Moreover, taking the value string by mutable lvalue reference in the call operator of your MapInserter is not a good idea: you don't want the argument to be modified, so you should either take it by const& or - my advice - take it by value and then move it into the returned pair, like so:
std::pair<int, std::string> operator()(std::string value)
{
return {count++, std::move(value)};
}
Since std::pair's constructor is not explicit, you do not even need the call to std::make_pair() in this case.
I have a map defined as map<string, map<string,int> > grandMap; and I need to check if the internal map has a key term. If so, count it. Here's what I tried already:
auto countOccurrences(string term) -> int
{
int count = 0;
for(auto entry : grandMap)
{
if(entry->second.find(term)!=entry->second.end())
{
count++;
cout << "counted" << endl;
}
}
return count;
}
But I get the following error(s):
415.cpp:50:11: error: base operand of '->' has non-pointer type 'std::pair<const std::basic_string<char>, std::map<std::basic_string<char>, int> >'
415.cpp:50:37: error: base operand of '->' has non-pointer type 'std::pair<const std::basic_string<char>, std::map<std::basic_string<char>, int> >'
...which clearly points to my attempt to get the second of entry, which I thought would be an element of the grandMap, but it doesn't seem to work quite how I'd like...
So what is the right way to go about this?
The problem is that you are using operator -> instead of operator .:
if (entry.second.find(term) != entry.second.end())
// ^ ^
Also, to avoid premature pessimization (see this GoTW by Herb Sutter for a definition), you should accept the term argument by reference to const, and you should also use auto const& in the range-based for loop.
Moreover, the count_if standard algorithm seems to be what you are looking for:
// Using C++14's return type deduction for regular functions...
auto count(std::string const& term)
{
return std::count_if(std::begin(grandMap), std::end(grandMap),
[&] (decltype(grandMap)::value_type const& entry)
{
return (entry.second.find("hello") != std::end(entry.second));
});
}
Here is a complete program:
#include <map>
#include <string>
#include <algorithm>
#include <iostream>
std::map<std::string, std::map<std::string, int>> grandMap =
{ { "1", { { "hello", 42 }, { "hi", 1337 } } },
{ "2", { { "hello", 42 }, { "hello", 1729 } } }};
auto count(std::string const& term)
{
return std::count_if(std::begin(grandMap), std::end(grandMap),
[&] (decltype(grandMap)::value_type const& entry)
{
return (entry.second.find("hello") != std::end(entry.second));
});
}
int main()
{
std::cout << count("hello");
}
And the corresponding live example.