how could I initialize unordered_map< vector<int> >? - c++

I met a question in my work
There is an unordered_map on vector<int> in my c++ class
like this unordered_map < int, vector<int> >
so how could I initialize the nested container so that when I insert a key to the hash table
and the value(vector) will be ten zero ?

You can use list initialization:
std::unordered_map<int, std::vector<int>> m
{ { 2, std::vector<int>(10, 0) }
, { 5, std::vector<int>(10, 0) }
, { 6, std::vector<int>(10, 0) }
, { 9, std::vector<int>(10, 0) }
};

It's very simple:
std::unordered_map<int, std::vector<int>> my_map;
my_map[123] = std::vector<int>{1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
Now my_map will contain one entry, with the key 123 and data being a vector containing ten entries.

Do not allow users to access the map directly, make them go through an accessor so you can ensure the vector gets populated how you want:
class FooBar
{
public:
// access the map
std::vector<int>& operator[](int n);
private:
std::unordered_map<int, std::vector<int>> map;
};
std::vector<int>& FooBar::operator[](int n)
{
auto iter = map.find(n);
if (iter == map.end()) // index not found, so insert it
iter = map.emplace(n, std::vector<int>(10, 0)).first;
return *iter;
}

According to what you stated in the comments you need a fixed size array.
Here a small example:
#include <array>
#include <unordered_map>
#include <iostream>
int main(int, char const *[])
{
std::unordered_map<int, std::array<int, 10>> the_map;
std::cout << the_map[0][1] << std::endl;
the_map[0][2]++;
std::cout << the_map[0][2] << std::endl;
return 0;
}
Output will be:
0
1
If you want to change the default value you can do something like:
struct my_array : public std::array<int, 10> { my_array() { fill(2); } };
int main(int , char const *[])
{
std::unordered_map<int, my_array> the_map;
std::cout << the_map[0][1] << std::endl;
the_map[0][2]++;
std::cout << the_map[0][2] << std::endl;
return 0;
}
Output:
2
3
Not my favorite choice, but you can do it this way.

Related

How can I create a 1D vector from a 2D vector in C++?

If I have a 2D vector of size n x m and I want to create a 1D vector of size n with all the elements in column 0 of 2D vector, how can I achieve this with one line of code?
Basically, I want to minimize the 3 lines in the body of the function into 1 line.
vector<int> jimOrders(vector<vector<int>> orders) {
vector<int> res;
for (auto i:orders) res.push_back(i[1]);
return res;
}
This could be a way:
Create a new vector with a size equals to the original vector (only that this won't be a vector of vectors of int, just a vector of int).
Then fill it with the contents of v[row][0], v being the original vector.
[Demo]
#include <algorithm> // generate
#include <iostream> // cout
#include <vector>
int main()
{
std::vector<std::vector<int>> v{ {1, 2}, {3, 4}, {5, 6} };
std::vector<int> w(v.size());
std::generate(std::begin(w), std::end(w), [row=0, &v]() mutable { return v[row++][0]; });
for (auto&& i : w) { std::cout << i << " "; } std::cout << "\n";
}
In a bit more generic way, still using vector of vectors though:
[Demo]
#include <algorithm> // generate
#include <iostream> // cout
#include <vector>
template <typename T>
auto slice_column(const std::vector<std::vector<T>>& v, size_t col)
{
std::vector<T> ret(v.size());
std::generate(std::begin(ret), std::end(ret), [row=0, &v, &col]() mutable { return v[row++][col]; });
return ret;
}
int main()
{
std::vector<std::vector<int>> v{ {1, 2}, {3, 4}, {5, 6} };
auto w{ slice_column(v, 0) };
for (auto&& i : w) { std::cout << i << " "; } std::cout << "\n";
}
I want to create a 1D vector of size n with all the elements in column 0 of 2D vector
This answer focuses on the above.
Create a view over the first element in each vector and return {begin(), end()} for that view to create the resulting vector from those iterators:
#include <ranges> // std::views::transform
#include <vector>
template<class T>
std::vector<T> jimOrders(const std::vector<std::vector<T>>& orders) {
auto view = orders | std::views::transform([](const auto& i) { return i[0]; });
return {view.begin(), view.end()};
}
Demo

can I make std::list insert new elements with order ? or got to use std::sort?

If I want to use std::list and that the new elements inserted to the list will be inserted to the right position in relation to a compare function - can I do it ?
or I have to use std::sort after each insertion?
You can use:
std::set if your elements a immutable
std::map if your elements have immutable keys, but should have mutable values
std::list and looking up the insertion position
std::list with std::lower_bound:
#include <algorithm>
#include <list>
#include <iostream>
int main()
{
std::list<int> list;
int values[] = { 7, 2, 5,3, 1, 6, 4};
for(auto i : values)
list.insert(std::lower_bound(list.begin(), list.end(), i), i);
for(auto i : list)
std::cout << i;
std::cout << '\n';
}
Alternatively you may populate an entire std::vector and sort it afterwards (Note: std::sort can not operate on std::list::iterators, they do not provide random access):
#include <algorithm>
#include <vector>
#include <iostream>
int main()
{
std::vector<int> vector = { 7, 2, 5,3, 1, 6, 4};
std::sort(vector.begin(), vector.end());
for(auto i : vector)
std::cout << i;
std::cout << '\n';
}
Note: The performance of a list with manual lookup of the insertion position is the worst O(N²).
Yes you can. Try something like following, just change compare function and type if needed.
#include <list>
inline
int compare(int& a, int&b) {
return a - b;
}
template<typename T>
void insert_in_order(std::list<T>& my_list, T element, int (*compare)(T& a, T&b)) {
auto begin = my_list.begin();
auto end = my_list.end();
while ( (begin != end) &&
( compare(*begin,element) < 0 ) ) {
++begin;
}
my_list.insert(begin, element);
}
int main() {
std::list<int> my_list = { 5,3,2,1 };
my_list.sort(); //list == { 1,2,3,5}
insert_in_order<int>(my_list, 4, &compare); //list == {1,2,3,4,5}
}
You have three options:
Sort after every insertion
Find the right index and insert at that index
Use an std::set (recommended)
Example for third option:
#include <iostream>
#include <set>
int main ()
{
int myints[] = {75,23,65,42,13};
std::set<int> myset (myints,myints+5);
std::cout << "myset contains:";
for (std::set<int>::iterator it=myset.begin(); it!=myset.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
Output:
myset contains: 13 23 42 65 75

C++ std::map and std::pair<int, int> as Key

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

How do I convert values from a vector to a map in c++?

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.

How can I sort an STL map by value?

How can I implement STL map sorting by value?
For example, I have a map m:
map<int, int> m;
m[1] = 10;
m[2] = 5;
m[4] = 6;
m[6] = 1;
I'd like to sort that map by m's value. So, if I print the map, I'd like to get the result as follows:
m[6] = 1
m[2] = 5
m[4] = 6
m[1] = 10
How can I sort the map in this way? Is there any way that I can deal with the key and value with sorted values?
Dump out all the key-value pairs into a set<pair<K, V> > first, where the set is constructed with a less-than functor that compares the pair's second value only. That way, your code still works even if your values aren't all distinct.
Or dump the key-value pairs into a vector<pair<K, V> >, then sort that vector with the same less-than functor afterwards.
You can build a second map, with the first map's values as keys and the first map's keys as values.
This works only if all values are distinct. If you cannot assume this, then you need to build a multimap instead of a map.
I wonder how can I implement the STL map sorting by value.
You can’t, by definition. A map is a data structure that sorts its element by key.
You should use Boost.Bimap for this sort of thing.
Based on #swegi's idea, I implemented a solution in c++11 using a multimap:
map<int, int> m = {{1, 10}, {2, 5}, {4, 6}, {6, 1}};
multimap<int, int> mm;
for(auto const &kv : m)
mm.insert(make_pair(kv.second, kv.first)); // Flip the pairs.
for(auto const &kv : mm)
cout << "m[" << kv.second << "] = " << kv.first << endl; // Flip the pairs again.
Code on Ideone
I also implemented a C++11 solution based on #Chris' idea using a vector of pairs. For correct sorting, I provide a lambda expression as comparison functor:
map<int, int> m = {{1, 10}, {2, 5}, {4, 6}, {6, 1}};
using mypair = pair<int, int>;
vector<mypair> v(begin(m), end(m));
sort(begin(v), end(v), [](const mypair& a, const mypair& b) { return a.second < b.second; });
for(auto const &p : v)
cout << "m[" << p.first << "] = " << p.second << endl;
Code on Ideone
The first solution is more compact, but both solutions should have roughly the same performance. Inserting into a multimap is of O(log n), but this has to be done for n entries, resulting in O(n log n). Sorting the vector in the second solution also results in O(n log n).
I also gave a try to #Chris' idea on using a set of pairs. However, it won't work if the values aren't all distinct. Using a functor that compares only the pair's second element doesn't help. If you first insert make_pair(1, 1) into the set and then try to insert make_pair(2, 1), then the second pair won't be inserted, because both pairs are seen as identical by that set. You can see that effect here on Ideone.
I've just done a similar question in my c++ book. The answer I came up with might not be very efficient though:
int main()
{
string s;
map<string, int> counters;
while(cin >> s)
++counters[s];
//Get the largest and smallest values from map
int beginPos = smallest_map_value(counters);
int endPos = largest_map_value(counters);
//Increment through smallest value to largest values found
for(int i = beginPos; i <= endPos; ++i)
{
//For each increment, go through the map...
for(map<string, int>::const_iterator it = counters.begin(); it != counters.end(); ++it)
{
//...and print out any pairs with matching values
if(it->second == i)
{
cout << it->first << "\t" << it->second << endl;
}
}
}
return 0;
}
//Find the smallest value for a map<string, int>
int smallest_map_value(const map<string, int>& m)
{
map<string, int>::const_iterator it = m.begin();
int lowest = it->second;
for(map<string, int>::const_iterator it = m.begin(); it != m.end(); ++it)
{
if(it->second < lowest)
lowest = it->second;
}
return lowest;
}
//Find the largest value for a map<string, int>
int largest_map_value(const map<string, int>& m)
{
map<string, int>::const_iterator it = m.begin();
int highest = it->second;
for(map<string, int>::const_iterator it = m.begin(); it != m.end(); ++it)
{
if(it->second > highest)
highest = it->second;
}
return highest;
}
Create another map, provide a less() function based on the value not key, AND the function should return true if the value1 <= value2 (not strictly < ). In this case, elements with non-distinct values can be sorted as well.
I have found this in thispointer. The example sorts a std::map< std::string,int> by all the int values.
#include <map>
#include <set>
#include <algorithm>
#include <functional>
int main() {
// Creating & Initializing a map of String & Ints
std::map<std::string, int> mapOfWordCount = { { "aaa", 10 }, { "ddd", 41 },
{ "bbb", 62 }, { "ccc", 13 } };
// Declaring the type of Predicate that accepts 2 pairs and return a bool
typedef std::function<bool(std::pair<std::string, int>, std::pair<std::string, int>)> Comparator;
// Defining a lambda function to compare two pairs. It will compare two pairs using second field
Comparator compFunctor =
[](std::pair<std::string, int> elem1 ,std::pair<std::string, int> elem2)
{
return elem1.second < elem2.second;
};
// Declaring a set that will store the pairs using above comparision logic
std::set<std::pair<std::string, int>, Comparator> setOfWords(
mapOfWordCount.begin(), mapOfWordCount.end(), compFunctor);
// Iterate over a set using range base for loop
// It will display the items in sorted order of values
for (std::pair<std::string, int> element : setOfWords)
std::cout << element.first << " :: " << element.second << std::endl;
return 0;
}
One thing that could be done in some scenarios, is using a vector<pair<int, int>> rather than using maps<int, int>. In this way you lose the benefits of using map, such as the less lookup time but you can directly use comparator function with vector<pair<int, int>>
bool compare(pair<int, int> a, pair<int, int> b)
{
return (a.second < b.second);
}
Recently had to do this. I ended up using pointers...
Quick Benchmark Results
#include <iostream>
#include <type_traits>
#include <algorithm>
#include <map>
#include <vector>
using map_t = std::map<int,int>;
const map_t m
{
{ 5, 20 },
{ -18, 28 },
{ 24, 49 },
{ 17, 27 },
{ 23, 46 },
{ 8, 16 },
{ -13, 11 },
{ -22, 32 },
{ 12, 45 },
{ -2, 19 },
{ 21, 11 },
{ -12, 25 },
{ -20, 8 },
{ 0, 29 },
{ -5, 20 },
{ 13, 26 },
{ 1, 27 },
{ -14, 3 },
{ 19, 47 },
{ -15, 17 },
{ 16, 1 },
{ -17, 50 },
{ -6, 40 },
{ 15, 24 },
{ 9, 10 }
};
template<typename T>
void sort_values_using_vector(T const& m)
{
using map_t = T;
using sort_t = std::vector<std::pair<typename map_t::key_type,
typename map_t::mapped_type>>;
sort_t sorted{ m.begin(), m.end() };
std::sort(sorted.begin(), sorted.end(),
[](auto const& lhs, auto const& rhs)
{
return lhs.second < rhs.second;
});
}
template<typename T>
void sort_values_using_multimap(T const& m)
{
using map_t = T;
using sort_t = std::multimap<typename map_t::mapped_type,
typename map_t::key_type>;
sort_t sorted;
for (auto const& kv : m)
{
sorted.insert(std::make_pair(kv.second, kv.first));
}
}
template<typename T>
void sort_values_using_ptrs(T const& m)
{
using map_t = T;
using ptr_t = std::add_pointer_t
<std::add_const_t<typename map_t::value_type>>;
using sort_t = std::vector<ptr_t>;
sort_t sorted;
sorted.reserve(m.size());
for (auto const& kv : m)
{
sorted.push_back(std::addressof(kv));
}
std::sort(sorted.begin(), sorted.end(),
[](auto const& lhs, auto const& rhs)
{
return lhs->second < rhs->second;
});
}
template<typename T>
void sort_values_using_refs(T const& m)
{
using map_t = T;
using ref_t = std::reference_wrapper
<std::add_const_t<typename map_t::value_type>>;
using sort_t = std::vector<ref_t>;
sort_t sorted{ m.begin(), m.end() };
std::sort(sorted.begin(), sorted.end(),
[](auto const& lhs, auto const& rhs)
{
return lhs.get().second < rhs.get().second;
});
}
static void copy_to_vector(benchmark::State& state) {
// Code inside this loop is measured repeatedly
for (auto _ : state) {
sort_values_using_vector(m);
}
}
BENCHMARK(copy_to_vector);
static void copy_flipped_to_multimap(benchmark::State& state) {
// Code inside this loop is measured repeatedly
for (auto _ : state) {
sort_values_using_multimap(m);
}
}
BENCHMARK(copy_flipped_to_multimap);
static void copy_ptrs_to_vector(benchmark::State& state) {
// Code inside this loop is measured repeatedly
for (auto _ : state) {
sort_values_using_ptrs(m);
}
}
BENCHMARK(copy_ptrs_to_vector);
static void use_refs_in_vector(benchmark::State& state) {
// Code inside this loop is measured repeatedly
for (auto _ : state) {
sort_values_using_refs(m);
}
}
BENCHMARK(use_refs_in_vector);
This code uses custom sorting function to sort map by values
// Comparator function to sort pairs
// according to value
bool comp(pair<int, int>& a,
pair<int, int>& b)
{
return a.second < b.second;
}
// Function to sort the map according
// to value in a (key-value) pair
void customSort(map<int, int>& m)
{
vector<pair<int, int>> a;
for(auto x:m)
a.push_back(make_pair(x.first,x.second));
sort(a.begin(), a.end(), comp);
for (auto x:a) {
cout << x.first<<" "<<x.second<<endl;
}
}