I am trying to remove entries from an unordered_map. A vector holds the keys that need to be removed from the unordered_map. I am trying to use for_each to iterate through the vector and call erase on the unordered_map.
#include <unordered_map>
#include <vector>
#include<algorithm>
int main()
{
std::unordered_map<int, bool> sample_map = { {0, false}, {1, true}, {2,false}};
std::vector keys_to_delete = { 0, 2};
std::for_each(keys_to_delete.begin(), keys_to_delete.end(), &sample_map.erase);
}
I receive the error:
note: couldn't deduce template parameter '_Funct' std::for_each(keys_to_delete.begin(), keys_to_delete.end(), &sample_map.erase);
How do I correctly bind sample_map's erase function?
You're missing a template argument for the vector key_to_delete.
Anyways, this problem might be simpler if you manually wrote the code that looped through each key and called the function erase.
However, if you'd like to use std::for_each then you could bind it to the correct function to be called. In this case, one has to static_cast to get the correct function since erase has multiple overloads.
#include <unordered_map>
#include <vector>
#include<algorithm>
#include <functional>
#include <iostream>
int main()
{
std::unordered_map<int, bool> sample_map = { { 0, false },{ 1, true },{ 2,false } };
std::vector<int> keys_to_delete = { 0, 2 };
using type = std::unordered_map<int, bool>;
std::for_each(keys_to_delete.begin(), keys_to_delete.end(), std::bind(static_cast<std::size_t(type::*)(const int&)>(&type::erase), &sample_map, std::placeholders::_1));
}
The way to do what you want is to use a lambda like so:
std::for_each(keys_to_delete.begin(), keys_to_delete.end(), [&](const auto& key) { sample_map.erase(key); });
std::for_each is not quite suitable there. The code would be cleaner with for.
#include <unordered_map>
#include <vector>
#include<algorithm>
int main()
{
std::unordered_map<int, bool> sample_map = { {0, false}, {1, true}, {2,false}};
std::vector<int> keys_to_delete = { 0, 2};
for (auto key : keys_to_delete)
sample_map.erase(key);
}
With using for_each the code will be heavy for understanding. std::unordered_map::erase has overloads, thus it can not be used directly, you would have to create a functional object that calls suitable overloaded method, or use lambda.
Related
I have a std::vector of std::variant elements with type int or std::set<int>. I want to loop over this vector and insert an extra item if the iterated element is of type std::set<int>. However, it seems that querying the index at run-time is not allowed. How can I achieve this?
#include <variant>
#include <set>
#include <vector>
int main()
{
using Variants = std::variant<int, std::set<int>>;
std::vector<Variants> var_vec;
var_vec.push_back(999);
std::set<int> a = {0,1,2};
var_vec.push_back(a);
for (int i = 0; i < var_vec.size(); ++i)
{
// if the element var_vec[i] is of type std::set<int>
if (var_vec[i].index() == 1) var_vec[i].insert(888); // !ERROR! How to achieve this?
}
return 0;
}
Error message:
error: '__gnu_cxx::__alloc_traits<std::allocator<std::variant<int, std::set<int, std::less<int>, std::allocator<int> > > >, std::variant<int, std::set<int, std::less<int>, std::allocator<int> > > >::value_type' {aka 'class std::variant<int, std::set<int, std::less<int>, std::allocator<int> > >'} has no member named 'insert'
There's nothing wrong with calling index() at runtime, this happens all the time. The real problem is:
var_vec[i].insert(888);
var_vec[i] is a std::variant. It does not have a method called insert().
std::get<1>(var_vec[i]).insert(888);
This gives you the variant's set, which will happily allow you to insert() something.
One problem with this overall approach is that if you, for some reason, want to modify your variant, and that std::set is no longer its 2nd alternative, for some reason, all of the above logic will break again.
You should consider using std::holds_alternative, instead of index(), and using a type with std::get instead of an index, which will automatically adapt to this kind of a change.
I want to loop over this vector and insert an extra item if the iterated element is of type std::set. However, it seems that querying the index at run-time is not allowed. How can I achieve this?
You don't need to check the type at runtime, you could just use std::visit() and check if the variant's type is std::set<int> at compile-time:
// Others headers go here...
#include <type_traits>
// ...
for (int i = 0; i < var_vec.size(); ++i)
// if the element var_vec[i] is of type std::set<int>
std::visit([](auto&& arg) {
if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, std::set<int>>)
arg.insert(888);
}, var_vec[i]);
The filtering can be done via ranges::views::filter, and we can use std::visit to make up a predicate.
To do so we need to feed std::visit with a predicate on the types. A predicate on types can be made up as a set of overlaoded functions that each takes a type and all return a bool; you can create it via boost::hana::overload.
However, to filter the variants we need to feed std::visit only with its first argument, and to leave its second parameter free; one way to do it is to wrap std::visit in a nested lambda, like this:
auto visit_with = [](auto const& fs){
return [&fs](auto const& xs) {
return std::visit(fs, xs);
};
};
Another way is to use BOOST_HOF_LIFT and boost::hana::curry the result to make it partially applicabale, which is all done in one line:
auto visit_with = curry<2>(BOOST_HOF_LIFT(std::visit));
Here's the complete code:
#include <boost/hana/functional/overload.hpp>
#include <boost/hana/functional/partial.hpp>
#include <iostream>
#include <range/v3/view/filter.hpp>
#include <set>
#include <variant>
#include <vector>
using namespace boost::hana;
using namespace ranges::views;
int main()
{
using Variants = std::variant<int, std::set<int>>;
std::vector<Variants> var_vec;
var_vec.push_back(999);
std::set<int> a = {0,1,2};
var_vec.push_back(a);
// predicate
auto constexpr true_for_sets = overload(
[](std::set<int> const&){ return true; },
[](auto const&){ return false; });
// function object wrapping std::visit
auto visit_with = curry<2>(BOOST_HOF_LIFT(std::visit));
// filter
auto var_vec_out = var_vec | filter(visit_with(true_for_sets));
}
And this is all about the filtering you mention in the title. However, in the body of the question, you're not actually doing a filtering, but something else, as you are modifying the set as you traverse it.
As was already pointed out you're calling insert on an std::variant and not the std::set.
One approach I'd use is using std::get_if like so:
#include <variant>
#include <set>
#include <vector>
int main( ) {
using Variants = std::variant<int, std::set<int>>;
std::vector<Variants> var_vec;
var_vec.push_back(999);
std::set<int> a = {0,1,2};
var_vec.push_back(a);
for ( auto& v : var_vec ) {
// If the variant does not hold a set, get_if returns a nullptr.
if ( auto set{ std::get_if<std::set<int>>( &v ) } ) {
set->insert( 888 );
}
}
// Or you could use holds_alternative with get, but this isn't as clean.
for ( auto& v : var_vec ) {
if ( std::holds_alternative<std::set<int>>( v ) ) {
auto set{ std::get<std::set<int>>( v ) };
set.insert( 999 );
}
}
}
This allows you to both test for the type and use the contained alternative.
The vector constructor calls in the statement below construct vectors with 50 elements of identical values in each vector as values in the sviMap. The statement repeats vector<int> three times. Is there a more concise way to achieve this overall initialization of the sviMap where vector<int> appears only once in the statement?
map<string, vector<int>> sviMap { {"Leo", vector<int>(50, 101)} , {"Brad", vector<int>(50, 201)} };
Strictly speaking, no. You need to have template specialization and two instances of the vector.
But if you need to have "this text once", this workaround could work for you:
using vi = vector<int>;
map<string, vi> sviMap{ {"Leo", vi(50, 101)} , {"Brad", vi(50, 201)} };
Formally, this is not "the statement", but two statements, but the vector appears only once. If your goal is to have one point for changes, this could work.
I wanted to correct my mistake, so here's another solution.
And this one will result in vectors of the correct length.
I just used inheritance to create a vector type with a constructor that has the desired behavior, so you can then use initializer lists without extra specifics
#include <cassert>
#include <map>
#include <vector>
#include <string>
template<typename type_t>
class my_vector :
public std::vector<type_t>
{
public:
my_vector() = default;
my_vector(const size_t size, const type_t value) :
std::vector<type_t>(size, value)
{
}
};
int main()
{
std::map<std::string, my_vector<int>> sviMap{ { "Leo", {50, 101}}, { "Brad", {50, 201}} };
auto vec = sviMap["Leo"];
auto value = vec[23];
}
You can do it like this, just use nested initializers
#include <map>
#include <vector>
#include <string>
int main()
{
std::map<std::string, std::vector<int>> sviMap
{
{"Leo", {50, 101}},
{"Brad", {50, 201}}
};
auto vec = sviMap["Leo"];
}
Say I have
class Value;
class Key;
class MyClass {
private:
std::map<Key,Value> my_map;
....
}
Inside of MyClass methods I have a very convenient way to iterate through values of my_map by saying
for( auto& value: my_map | boost::adaptors::map_values) {
...
}
However I would like to have a method of MyClass that would essentially output
my_map | boost::adaptors::map_values
and allow convenient value iteration outside of MyClass methods. How do I declare such a method? Would I need to implement some sort of pseudo container and corresponding iterator or there is a shortcut?
Basically, you have two good choices. You can provide users with a nicely decoupled interface by using boost's any_range, or else if you need the best performance, you can give clients direct access to the adapted range using c++11 decltypes. In the first case, clients won't have to change if you change implementation, but this comes at a cost of extra redirection.
using any_range:
#include <boost/range.hpp>
#include <map>
#include <boost/range/adaptor/map.hpp>
#include <boost/range/any_range.hpp>
class Foo
{
std::map<int, int> my_map;
boost::any_range<int, boost::forward_traversal_tag, int&, std::ptrdiff_t> Values()
{ return my_map | boost::adaptors::map_values; }
};
giving direct access (awkward syntax here is because you require VS2013, which doesn't support member variables in unevaluated context at class scope):
#include <boost/range.hpp>
#include <map>
#include <boost/range/adaptor/map.hpp>
class Foo
{
std::map<int, int> my_map;
auto Values() -> decltype(my_map | boost::adaptors::map_values)
{ return my_map | boost::adaptors::map_values; }
};
It isn't strictly necessary to use a pseudo container or an iterator adapter, such as boost::iterator_range. Although using a proper adapter such as iterator_range would theoretically be more correct and versatile, and will not violate the principle of least knowledge as applied to objects, you may want to avoid it because of an additional indirection, or maybe because your iterator is generally only a single-pass range and not a forward range.
So if you wish to use the adapted range directly you can simply use decltype to deduce the iterator type already returned by the adaptor:
#include <iostream>
#include <map>
#include <boost/range/adaptor/map.hpp>
class A {
std::map<int,int> my_map = { {0, 1}, {2, 3} };
public:
decltype(my_map | boost::adaptors::map_values)
values() { return my_map | boost::adaptors::map_values; }
};
int main() {
for (const auto& v : A().values())
std::cout << "v = " << v << std::endl;
return 0;
}
/* Output:
v = 1
v = 3
*/
And if you want the exposed values to be a const member function it's slightly more intricate:
class A {
...
decltype(const_cast<const std::map<int,int>&>(my_map) | boost::adaptors::map_values)
values() const { return my_map | boost::adaptors::map_values; }
}
I have two arrays, as follows:
std::array<int,6> A,B;
//Fill A with random data
//File B with random data
For whatever reason, I'd like some kind of container object which lets me access both of the vectors individually, but also to iterate over their union, allowing for actions such as follows:
union_container uc(A,B);
for(unioned::iterator i=uc.begin();i!=uc.end();++i)
*i+=1;
uc.first()[2]=4;
uc.second()[4]=5;
I could code this unioned class myself, but perhaps there is already a library which allows this?
Using Boost zip iterators is one way to do this.
#include <array>
#include <functional>
#include <iostream>
#include <boost/tuple/tuple.hpp>
#include <boost/iterator/zip_iterator.hpp>
template<typename T>
using ZipIt = boost::zip_iterator< boost::tuple<T*, T*> >;
template<typename T>
using ZipRef = decltype(*ZipIt<T>());
template<typename T>
void operator+=(ZipRef<T> z, T const& v)
{
z.template get<0>() += v;
z.template get<1>() += v;
}
int main()
{
std::array<int, 6> a = { 1, 3, 5, 7, 9, 11 };
std::array<int, 6> b = { 2, 4, 6, 8, 10, 12 };
std::for_each(
boost::make_zip_iterator(boost::make_tuple(std::begin(a), std::begin(b))),
boost::make_zip_iterator(boost::make_tuple(std::end(a), std::end(b))),
[](ZipRef<int> z){ z += 1; }
);
std::copy(std::begin(a), std::end(a), std::ostream_iterator<int>(std::cout, ",")); std::cout << "\n";
std::copy(std::begin(b), std::end(b), std::ostream_iterator<int>(std::cout, ",")); std::cout << "\n";
a[2] = 4;
b[4] = 5;
}
Online output.
Note that the code above is not as general as I've would liked, since the jump to variadic templates and general iterator types proved a bit hairy (left as an exercise!) This has mainly to do with the fact that boost::zip_iterator uses some tricky internal facades around boost::tuple. For this reason I also used decltype in the template alias for ZipRef to avoid having to write such nasty types inside the std::for_each lambda expression.
I have a vector of maps:
typedef map<string, string> aMap;
typedef vector<aMap> rVec;
rVec rows;
How can I remove some elements from rows?
The following code does not work.
struct remove_it
{
bool operator() (rVec& rows)
{
// Validation code here!
}
};
rVec::iterator it = remove(rows.begin(), rows.end(), remove_it());
rows.erase(it, rows.end());
I got the following error.
error: no matching function for call to 'remove(std::vector<s
td::map<std::basic_string<char>, std::basic_string<char> > >::iterator, std::vec
tor<std::map<std::basic_string<char>, std::basic_string<char> > >::iterator, mai
n(int, char**)::remove_it)'
Thanks.
1) First off: please provide a single compilable example.
Your code posted above is problematic as rVec and rowsVector have been interchanged (you would have seen that yourself if you had posted real code).
2) You are using the wrong remove. It should be remove_if
3) It is normal for the functor to be const
4) The operator() should get object of type aMap (as that is what is in your vector) not a reference back to the vector.
5) Don't be lazy add std:: in-front of objects in the standard namespace.
rather than using using namespace std;
#include <map>
#include <vector>
#include <string>
#include <algorithm>
typedef std::map<std::string, std::string> aMap;
typedef std::vector<aMap> rVec;
rVec rows;
struct remove_it
{
// Corrected type here
bool operator() (aMap const& row) const // const here
{
// Validation code here!
return true;
}
};
int main()
{
// _if herer
rVec::iterator it = std::remove_if(rows.begin(), rows.end(), remove_it());
rows.erase(it, rows.end());
}
remove expects a value. You are trying to use a functor, you need to use remove_if for that.
Also, your functor needs to accept an object of type aMap, not rVec.
remove_if is what you want, not remove.