I've the following C++ code:
struct MyStruct
{
int a[2];
int b[2];
};
std::map<std::pair<int, int> , MyStruct*> MyMap;
Now I run this loop on MyMap:
for(std::map<std::pair<int, int> , MyStruct*>::iterator itr = MyMap.begin(); itr != MyMap.end(); ++itr)
{
std::pair<int, int> p (itr->first().second, itr->first().first);
auto i = MyMap.find(p);
if(i != MyMap.end())
{
//do something
}
}
What I'm actually trying to do is forming a pair by swapping the elements of another pair , so for example I have a key pair(12,16) in MyMap and also another key pair(16,12); these two key exists in MyMap and I know for sure. But when I apply the above technique MyMap don't return the value corresponding to the swapped key, what I'm guessing that MyMap.find(p) is matching the pointer of Key; but is there a way so that I can force MyMap.find(p) to match the corresponding value in Key (pair) instead of matching the pointers in Key (pair) ? Or is there anything I'm doing wrong here ?
You have some imprecisions in your code, say, your MyStruct does not have a copy constructor, but contains arrays, itr->first() in your for loop, while first doesn't have a call operator, and others. The following code does what you want:
#include <array>
#include <map>
#include <utility>
#include <memory>
#include <stdexcept>
#include <iostream>
struct MyStruct
{
std::array<int, 2> a;
std::array<int, 2> b;
};
template <class T, class U>
std::pair<U, T> get_reversed_pair(const std::pair<T, U>& p)
{
return std::make_pair(p.second, p.first);
}
int main()
{
std::map<std::pair<int, int>, std::shared_ptr<MyStruct>> m
{
{
{12, 16},
std::make_shared<MyStruct>()
},
{
{16, 12},
std::make_shared<MyStruct>()
}
};
std::size_t count = 1;
for(const auto& p: m)
{
try
{
auto f = m.at(get_reversed_pair(p.first));
f -> a.at(0) = count++;
f -> b.at(0) = count++;
}
catch(std::out_of_range& e)
{
}
}
for(const auto& p: m)
{
std::cout << p.first.first << ' ' << p.first.second << " - ";
std::cout << p.second -> a.at(0) << ' ' << p.second -> b.at(0) << std::endl;
}
return 0;
}
Output:
12 16 - 3 4
16 12 - 1 2
Related
I have a std::multimap<int, X*> where X is a user-defined type. I want to find a specific key-value-pair within this multimap (i.e. an iterator pointing to this pair).
(A) Complete Example:
#include <map>
#include <algorithm>
class X {};
int main()
{
X myX;
std::multimap<int, X*> myMap;
auto it = std::find(myMap.begin(), myMap.end(), std::make_pair(5, &myX));
return 0;
}
However, this does not compile (gcc 12.2.1 with -std=gnu++2a):
no match for ‘operator==’ (operand types are ‘std::pair<const int, X*>’ and ‘const std::pair<int, X*>’)
So it seems to me somehow int gets converted to const int.
(B) Using std::find_if with a lamdba function with const int, the code compiles:
auto it = std::find_if(myMap.begin(), myMap.end(), [myX](std::pair<const int, X*>& node){ return 5 == node.first && (&myX) == node.second; } );
Questions:
Why is the type of the keys in the multimap const int and not int?
How to fix it in a more natural way than using a (complex) lambda function like in (B) or by first looking up by key and then searching within its values (as described by Igor Tandetnik in the comments)?
Why is the type of the keys in the multimap const int and not int?
Because the Key in all standard maps is immutable.
How to fix it in a more natural way than using a (complex) lambda function like in (B) or by first looking up by key and then searching within its values?
Here's a simplified example that doesn't use X* but X in the map:
#include <algorithm>
#include <iostream>
#include <map>
struct X {
bool operator==(const X& rhs) const { return m_value == rhs.m_value; }
int m_value;
};
int main() {
std::multimap<int, X> myMap{
{4, {1}}, {5, {2}}, {6, {3}}, {4, {4}}, {5, {5}}, {6, {6}}
};
// get the range of _Key_s equal to 5
auto [first, last] = myMap.equal_range(5);
// the lambda - here used to find the first entry with the _Value_ `X{2}`
auto cond = [](auto&& pair) { return pair.second == X{2}; };
if (auto it = std::find_if(first, last, cond); it != last)
{
auto& [k, v] = *it;
std::cout << k << ' ' << v.m_value << '\n';
}
}
Demo
If many Values may be found, just put the find_if in a loop:
for(;(first = std::find_if(first, last, cond)) != last; ++first) {
auto& [k, v] = *first;
std::cout << k << ' ' << v.m_value << '\n';
}
Demo
I have a std::map<std::string, std::vector<std::string>> and I need to perform a threaded task on this map by dividing the map into sub-maps and passing each sub-map to a thread.
With a std::vector<T> I would be able to get a sub-vector pretty easy, by doing this:
#include <vector>
#include <string>
int main(void)
{
size_t off = 0;
size_t num_elms = 100; // Made up value
std::vector<uint8_t> full; // Assume filled with stuff
std::vector<uin8t_t> sub(std::begin(full) + off, std::begin(full) + off + num_elms);
off = off + num_elms;
}
However, doing the same with std::map<T1, T2> gives a compilation error.
#include <vector>
#include <map>
#include <string>
int main(void)
{
size_t off = 0;
size_t num_elms = 100;
std::map<std::string, std::vector<std::string>> full;
std::map<std::string, std::vector<std::string>> sub(std::begin(full) + off,
std::begin(full) + off + num_elms);
off = off + num_elms;
}
It is the same with other std::map "types". Which, from what I have gathered, is down to the iterator.
What is possible is to extract the keys and do something similar to this solution:
#include <map>
#include <vector>
#include <string>
#include <iostream>
void print_map(const std::map<std::string, std::vector<std::string>>& _map)
{
for (const auto& [key, value] : _map)
{
std::cout << "key: " << key << "\nvalues\n";
for (const auto& elm : value)
{
std::cout << "\t" << elm << "\n";
}
}
}
void print_keys(const std::vector<std::string>& keys)
{
std::cout << "keys: \n";
for(const auto& key : keys)
{
std::cout << key << "\n";
}
}
int main(void)
{
std::map<std::string, std::vector<std::string>> full;
full["aa"] = {"aa", "aaaa", "aabb"};
full["bb"] = {"bb", "bbbbb", "bbaa"};
full["cc"] = {"cc", "cccc", "ccbb"};
full["dd"] = {"dd", "dd", "ddcc"};
print_map(full);
std::vector<std::string> keys;
for (const auto& [key, value] : full)
{
(void) value;
keys.emplace_back(key);
}
print_keys(keys);
size_t off = 0;
size_t num_elms = 2;
std::map<std::string, std::vector<std::string>> sub1 (full.find(keys.at(off)), full.find(keys.at(off + num_elms)));
off = off + num_elms;
std::map<std::string, std::vector<std::string>> sub2 (full.find(keys.at(off)), full.find(keys.at(off + num_elms -1)));
std::cout << "sub1:\n";
print_map(sub1);
std::cout << "sub2:\n";
print_map(sub2);
}
However, this has the potential to be extremely inefficient, as the map can be really big (10k+ elements).
So, is there a better way to replicate the std::vector approach with std::map?
A slightly different approach would be to use one of the execution policies added in C++17, like std::execution::parallel_policy. In the example below, the instance std::execution::par is used:
#include <execution>
// ...
std::for_each(std::execution::par, full.begin(), full.end(), [](auto& p) {
// Here you are likely using a thread from a built-in thread pool
auto& vec = p.second;
// do work with "vec"
});
With a slight adaption, you can reasonably easily pass ranges to print_map, and divide up your map by calling std::next on an iterator.
// Minimal range-for support
template <typename Iter>
struct Range {
Range (Iter b, Iter e) : b(b), e(e) {}
Iter b;
Iter e;
Iter begin() const { return b; }
Iter end() const { return e; }
};
// some shorter aliases
using Map = std::map<std::string, std::vector<std::string>>;
using MapView = Range<Map::const_iterator>;
// not necessarily the whole map
void print_map(MapView map) {
for (const auto& [key, value] : map)
{
std::cout << "key: " << key << "\nvalues\n";
for (const auto& elm : value)
{
std::cout << "\t" << elm << "\n";
}
}
}
int main(void)
{
Map full;
full["aa"] = {"aa", "aaaa", "aabb"};
full["bb"] = {"bb", "bbbbb", "bbaa"};
full["cc"] = {"cc", "cccc", "ccbb"};
full["dd"] = {"dd", "dd", "ddcc"};
// can still print the whole map
print_map({ map.begin(), map.end() });
size_t num_elms = 2;
size_t num_full_views = full.size() / num_elms;
std::vector<MapView> views;
auto it = full.begin();
for (size_t i = 0; i < num_full_views; ++i) {
auto next = std::next(it, num_elms);
views.emplace_back(it, next);
it = next;
}
if (it != full.end()) {
views.emplace_back(it, full.end());
}
for (auto view : views) {
print_map(view);
}
}
In C++20 (or with another ranges library), this can be simplified with std::ranges::drop_view / std::ranges::take_view.
using MapView = decltype(std::declval<Map>() | std::ranges::views::drop(0) | std::ranges::views::take(0));
for (size_t i = 0; i < map.size(); i += num_elms) {
views.push_back(map | std::ranges::views::drop(i) | std::ranges::views::take(num_elms));
}
I am trying to solve a question on leetcode which is finding the top k frequent elements. I think my code is correct but the output for a test case is failing.
Input: [ 4,1,-1,2,-1,2,3]
K=2
My answer comes out to be {1,-1} but the expected is {-1,2}. I am not sure where am i getting wrong.
struct myComp{
constexpr bool operator()(pair<int,int> & a,pair<int,int> &b)
const noexcept
{
if(a.second==b.second)
{
return a.first<b.first;
}
return a.second<b.second;
}
};
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int,int> mp;
for(int i=0;i<nums.size();i++)
{
mp[nums[i]]++;
}
priority_queue<pair<int,int>,vector<pair<int,int>>,myComp> minheap;
for(auto x:mp)
{
minheap.push(make_pair(x.second,x.first));
if(minheap.size()>k)
{
minheap.pop();
}
}
vector<int> x;
while(minheap.size()>0)
{
x.push_back(minheap.top().second);
minheap.pop();
}
return x;
link: https://leetcode.com/problems/top-k-frequent-elements
In the minheap, pairs of <frequency, element> are being pushed. Since we want to sort these pairs on basis of frequency, we need to compare on the basis of frequency only.
Let's say there are two pairs a and b. Then for normal sorting, the comparison would be :
a.first < b.first;
And for reverse sorting, the comparison would be :
a.first > b.first;
In case of min-heap, we need reverse sorting. Hence, the following comparator makes your code pass all the test cases :
struct myComp
{
constexpr bool operator()(pair<int,int> & a,pair<int,int> &b)
const noexcept
{
return a.first > b.first;
}
};
There are several issues with your code.
Obviously there is somewhere using namespace std; in your code. This should be avoided. You will find many posts here on SO explaining, why it this should not be done.
Then we need to qualify all elements from the std library with std::, which makes the scope very clear.
Next: You do not need your own sorting function. Since you insert the elements from the pair in swapped order into the std::priority_queue, the sorting criteria is valid for the counter part, not for the key value. So, your sorting function was anyway wrong, because it was sorting accodring to "second" and not to "first". But if we have a standard sorting, we do not need a special sorting algorithm. A std::pair has a less-than operator. So, the definition can be simply:
std::priority_queue<std::pair<int, int>> minheap;
Then, your if statement
if(minheap.size()>k)
{
minheap.pop();
}
is wrong. You will allow only k values to be inserted. And this are not necessarily the biggest ones. So, you need to insert all values from the std::unordered map. And then they are sorted.
With some cosmetic changes the code will look like the below:
#include <iostream>
#include <utility>
#include <unordered_map>
#include <vector>
#include <queue>
std::vector<int> topKFrequent(std::vector<int>& nums, size_t k) {
std::unordered_map<int, int> mp;
for (size_t i = 0; i < nums.size(); i++)
{
mp[nums[i]]++;
}
std::priority_queue<std::pair<int, int>> minheap;
for (auto x : mp)
{
minheap.push(std::make_pair(x.second, x.first));
}
std::vector<int> x;
for (size_t i{}; i< k; ++i)
{
x.push_back(minheap.top().second);
minheap.pop();
}
return x;
}
int main() {
std::vector data{ 4,1,-1,2,-1,2,3 };
std::vector result = topKFrequent(data, 2);
for (const int i : result) std::cout << i << ' '; std::cout << '\n';
return 0;
}
An additional solution
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <utility>
auto topKFrequent(std::vector<int>& nums, size_t k) {
// Count occurences
std::unordered_map<int, size_t> counter{};
for (const int& i : nums) counter[i]++;
// For storing the top k
std::vector<std::pair<int, size_t>> top(k);
// Get top k
std::partial_sort_copy(counter.begin(), counter.end(), top.begin(), top.end(),
[](const std::pair<int, size_t >& p1, const std::pair<int, size_t>& p2) { return p1.second > p2.second; });
return top;
}
// Test code
int main() {
std::vector data{ 4,1,-1,2,-1,2,3 };
for (const auto& p : topKFrequent(data, 2))
std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
return 0;
}
And of course, we do have also the universal solution for any kind of iterable container. Including the definition for type traits using SFINAE and checking for the correct template parameter.
#include <iostream>
#include <utility>
#include <unordered_map>
#include <algorithm>
#include <vector>
#include <iterator>
#include <type_traits>
// Helper for type trait We want to identify an iterable container ----------------------------------------------------
template <typename Container>
auto isIterableHelper(int) -> decltype (
std::begin(std::declval<Container&>()) != std::end(std::declval<Container&>()), // begin/end and operator !=
++std::declval<decltype(std::begin(std::declval<Container&>()))&>(), // operator ++
void(*std::begin(std::declval<Container&>())), // operator*
void(), // Handle potential operator ,
std::true_type{});
template <typename T>
std::false_type isIterableHelper(...);
// The type trait -----------------------------------------------------------------------------------------------------
template <typename Container>
using is_iterable = decltype(isIterableHelper<Container>(0));
// Some Alias names for later easier reading --------------------------------------------------------------------------
template <typename Container>
using ValueType = std::decay_t<decltype(*std::begin(std::declval<Container&>()))>;
template <typename Container>
using Pair = std::pair<ValueType<Container>, size_t>;
template <typename Container>
using Counter = std::unordered_map<ValueType<Container>, size_t>;
// Function to get the k most frequent elements used in any Container ------------------------------------------------
template <class Container>
auto topKFrequent(const Container& data, size_t k) {
if constexpr (is_iterable<Container>::value) {
// Count all occurences of data
Counter<Container> counter{};
for (const auto& d : data) counter[d]++;
// For storing the top k
std::vector<Pair<Container>> top(k);
// Get top k
std::partial_sort_copy(counter.begin(), counter.end(), top.begin(), top.end(),
[](const std::pair<int, size_t >& p1, const std::pair<int, size_t>& p2) { return p1.second > p2.second; });
return top;
}
else
return data;
}
int main() {
std::vector testVector{ 1,2,2,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,6,7 };
for (const auto& p : topKFrequent(testVector, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
std::cout << '\n';
double cStyleArray[] = { 1.1, 2.2, 2.2, 3.3, 3.3, 3.3 };
for (const auto& p : topKFrequent(cStyleArray, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
std::cout << '\n';
std::string s{"abbcccddddeeeeeffffffggggggg"};
for (const auto& p : topKFrequent(s, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
std::cout << '\n';
double value = 12.34;
std::cout << topKFrequent(value,2) << "\n";
return 0;
}
Developed and tested with Microsoft Visual Studio Community 2019, Version 16.8.2.
Additionally compiled and tested with clang11.0 and gcc10.2
Language: C++17
I want to do something like this. Is there a stl algorithm that does this easily?
for each(auto aValue in aVector)
{
aMap[aValue] = 1;
}
If you have a vector of pairs, where the first item in the pair will be the key for the map, and the second item will be the value associated with that key, you can just copy the data to the map with an insert iterator:
std::vector<std::pair<std::string, int> > values {
{"Jerry", 1},
{ "Jim", 2},
{ "Bill", 3} };
std::map<std::string, int> mapped_values;
std::copy(values.begin(), values.end(),
std::inserter(mapped_values, mapped_values.begin()));
or, you could initialize the map from the vector:
std::map<std::string, int> m2((values.begin()), values.end());
Maybe like this:
std::vector<T> v; // populate this
std::map<T, int> m;
for (auto const & x : v) { m[x] = 1; }
You might std::transform the std::vector into a std::map
std::vector<std::string> v{"I", "want", "to", "do", "something", "like", "this"};
std::map<std::string, int> m;
std::transform(v.begin(), v.end(), std::inserter(m, m.end()),
[](const std::string &s) { return std::make_pair(s, 1); });
This creates std::pairs from the vector's elements, which in turn are inserted into the map.
Or, as suggested by #BenFulton, zipping two vectors into a map
std::vector<std::string> k{"I", "want", "to", "do", "something", "like", "this"};
std::vector<int> v{1, 2, 3, 4, 5, 6, 7};
std::map<std::string, int> m;
auto zip = [](const std::string &s, int i) { return std::make_pair(s, i); };
std::transform(k.begin(), k.end(), v.begin(), std::inserter(m, m.end()), zip);
Assuming items in vector are related in order, maybe this example can help :
#include <map>
#include <vector>
#include <string>
#include <iostream>
std::map<std::string, std::string> convert_to_map(const std::vector<std::string>& vec)
{
std::map<std::string, std::string> mp;
std::pair<std::string, std::string> par;
for(unsigned int i=0; i<vec.size(); i++)
{
if(i == 0 || i%2 == 0)
{
par.first = vec.at(i);
par.second = std::string();
if(i == (vec.size()-1))
{
mp.insert(par);
}
}
else
{
par.second = vec.at(i);
mp.insert(par);
}
}
return mp;
}
int main(int argc, char** argv)
{
std::vector<std::string> vec;
vec.push_back("customer_id");
vec.push_back("1");
vec.push_back("shop_id");
vec.push_back("2");
vec.push_back("state_id");
vec.push_back("3");
vec.push_back("city_id");
// convert vector to map
std::map<std::string, std::string> mp = convert_to_map(vec);
// print content:
for (auto it = mp.cbegin(); it != mp.cend(); ++it)
std::cout << " [" << (*it).first << ':' << (*it).second << ']';
std::cout << std::endl;
return 0;
}
A very generic approach to this, since you didn't specify any types, can be done as follows:
template<class Iterator, class KeySelectorFunc,
class Value = std::decay_t<decltype(*std::declval<Iterator>())>,
class Key = std::decay_t<decltype(std::declval<KeySelectorFunc>()(std::declval<Value>()))>>
std::map<Key, Value> toMap(Iterator begin, Iterator end, KeySelectorFunc selector) {
std::map<Key, Value> map;
std::transform(begin, end, std::inserter(map, map.end()), [selector](const Value& value) mutable {
return std::make_pair(selector(value), value);
});
return map;
}
Usage:
struct TestStruct {
int id;
std::string s;
};
std::vector<TestStruct> testStruct = {
TestStruct{1, "Hello"},
TestStruct{2, "Hello"},
TestStruct{3, "Hello"}
};
std::map<int, TestStruct> map = toMap(testStruct.begin(), testStruct.end(),
[](const TestStruct& t) {
return t.id;
}
);
for (const auto& pair : map) {
std::cout << pair.first << ' ' << pair.second.id << ' ' << pair.second.s << '\n';
}
// yields:
// 1 1 Hello
// 2 2 Hello
// 3 3 Hello
The parameter selector function is used to select what to use for the key in the std::map.
Try this:
for (auto it = vector.begin(); it != vector.end(); it++) {
aMap[aLabel] = it;
//Change aLabel here if you need to
//Or you could aMap[it] = 1 depending on what you really want.
}
I assume this is what you are trying to do.
If you want to update the value of aLabel, you could change it in the loop. Also, I look back at the original question, and it was unclear what they wanted so I added another version.
Yet another way:
#include <map>
#include <vector>
#include <boost/iterator/transform_iterator.hpp>
int main() {
using T = double;
std::vector<T> v;
auto f = [](T value) { return std::make_pair(value, 1); };
std::map<T, int> m(boost::make_transform_iterator(v.begin(), f),
boost::make_transform_iterator(v.end(), f));
}
But I don't think it beats range-for loop here in terms of readability and execution speed.
For converting values of a array or a vector directly to map we can do like.
map<int, int> mp;
for (int i = 0; i < m; i++)
mp[a[i]]++;
where a[i] is that array we have with us.
I'm trying to solve a issue where I'm inserting chars in to a map of type <char, int>. If the char already exists in the map I will increase the int by 1. I have created my own comparator for prioritizing the elements within the map. The priority doesn't work in the way I hope it would work since in the end the output doesn't follow the order.
#include <iostream>
#include <string>
#include <map>
#include <iterator>
using namespace std;
struct classcomp {
bool operator()(const int& a, const int& b) const {
return a < b;
}
};
bool isPresent(map<char,int,classcomp> mymap, char c){
return (mymap.find('b') != mymap.end());
}
int main(){
string input="dadbadddddddcabca";
map<char,int,classcomp> mymap;
char temp;
for(string::iterator it = input.begin(); it!=input.end(); ++it){
temp = *it;
if(!isPresent(mymap, temp))
mymap.insert(pair<char,int>(*it,1));
else
mymap[temp]++;
}
for (auto& x: mymap) {
cout << x.first << ": " << x.second << '\n';
}
return 0;
}
Gives the following output:
a: 4
b: 2
c: 2
d: 8
std::map is designed to be sorted by key, and providing comparator for type of value does not change anything. imagine you have std::map<char,char>, how would you think you can provide comparator for value (if it would be possible)?
So solution would be to use container that allows to sort by multiple keys like boost::multi_index or just create another map - reversed:
#include <iostream>
#include <string>
#include <map>
#include <iterator>
using namespace std;
int main(){
string input="dadbadddddddcabca";
map<char,int> mymap;
for(string::iterator it = input.begin(); it!=input.end(); ++it){
mymap[*it]++;
}
map<int,char> reversemap;
for (auto& x: mymap) {
reversemap.insert( make_pair( x.second, x.first ) );
}
for (auto& x: reversemap ) {
cout << x.first << ": " << x.second << '\n';
}
return 0;
}
Notice that your pre-check for element existance is completely redundant, std::map operator[] creates new element and initializes it, if it does not exists.
You may notice that in output you are missing some values now (though they are sorted), if that is not what you need, change reversemap type from map to multimap, which allows key duplicates.
The comparator is used to sort the chars and not the ints.
It is sorting the keys and seems to work just fine - a b c d.
map sorts its entries by key, not value. The char keys get silently cast to int in your classcomp::operator()
Why
mymap.find('b') != mymap.end());
and not
mymap.find(c) != mymap.end());
Maybe this is what you wanted
int main() {
std::string input="dadbadddddddcabca";
typedef std::map< char, int > map_t;
map_t mymap;
char temp;
for ( std::string::const_iterator it = input.begin(), e = input.end(); it != e; ++it ) {
temp = *it;
mymap[ temp ] = mymap[ temp ] + 1; // Hopufuly operator[] inserts zero initialized value, if can't find a key
}
typedef std::pair< typename map_t::key_type, typename map_t::mapped_type > pair_t;
std::vector< pair_t > sortedByValue;
sortedByValue.assign( mymap.begin(), mymap.end() );
std::sort( sortedByValue.begin(), sortedByValue.end(), []( const pair_t & left, const pair_t & right ) {
return left.second < right.second;
// change to
// return left.second > right.second;
// for descend order
} );
for ( const auto & x: sortedByValue ) {
std::cout << x.first << ": " << x.second << std::endl;
}
}
LWS link