I am trying below code using ranges but it doesn't working.
// Code
std::map<int, std::string> m{ {1,"foo"},{42,"bar"},{7,"baz"} };
std::vector<int> keys;
// without using ranges
std::transform(begin(m), end(m), std::back_inserter(keys), [](auto val)
{
return val.first;
});
which is working fine. But,
// with using ranges
ranges::transform(m,std::back_inserter(keys), [](auto val)
{
return val.first;
});
it is not working with ranges??
I am using MSVC 2017 15.9.14
The range-v3 doesn't support std::back_insert_iterator, because it doesn't satisfy the library Iterator concept, see this issue. As it's pointed out in the discussion, this is supposed to be fixed with C++20.
You can fix this by either
keys.resize(3);
ranges::transform(m, keys.begin(), [](auto val) { return val.first; });
or, in my opinion preferable (as you can make keys const):
const std::vector<int> keys = m |
ranges::view::transform([](auto val){ return val.first; });
As a side note, consider passing the lambda parameter as a const-qualified reference to avoid unnecessary copies.
Related
In order to avoid mutable containers / states I currently wonder what's the closest thing to construct a const STL container from some input, e.g.
const vector<int> input = {2, 13, 7, 1};
What I'd like to do is something like this:
const auto transformed = generate_from<vector<string>>(
input.begin(), input.end(), to_string);
do_something(transformed);
While the approach you find the most would create a mutable object and modify it (what I'd like to avoid):
vector<string> bad_mutable_container;
for (const auto & elem : input) {
bad_mutable_container.push_back(to_string(input[elem]));
};
do_something(bad_mutable_container);
C++11 and newer provide std::generate and std::generate_n but they operate on a mutable object, so they don't solve my problem:
vector<string> bad_mutable_container(input.size());
generate_n(bad_mutable_container.begin(), input.size(), [&input, n=0] () mutable {
return to_string(input[n++]);
});
What you can do now is encapsulate that code in a function/lambda which gives you const-ness but also noisy boilerplate code:
const auto transformed = [&input] {
vector<string> bad_mutable_container;
for (const auto & elem : input) {
bad_mutable_container.push_back(to_string(elem));
};
return bad_mutable_container;
} ();
do_something(transformed);
I've expected to find at least some constructor for e.g. std::vector which I can use like this:
const auto transformed = vector<string>(input.size(), [&input, n=0] () mutable {
return to_string(input[n++]);
});
What would be the most modern C++ish approach to this today and why?
With boost::transform_iterator, you may do:
auto to_string_fun = [](const auto& e){ return std::to_string(e); };
const std::vector output(boost::transform_iterator(input.begin(), to_string_fun),
boost::transform_iterator(input.end(), to_string_fun));
Demo
With range-v3, you may do:
const std::vector<std::string> output = input
| ranges::view::transform([](int e){ return std::to_string(e); });
Demo
With the help of Jarod42's answer I found boost::copy_range together with boost::adaptors::transformed which can be used like range-v3:
const auto transformed = boost::copy_range<vector<string>>(
input |
boost::adaptors::transformed([] (const auto &elem) {
return to_string(elem);}));
Since I'm already using Boost I'll go with this solution otherwise I certainly would use range-v3 until it's becoming standard :)
I have some C++11 code like
std::vector<std::string> names;
std::map<std::string, std::string> first_to_last_name_map;
std::transform(names.begin(), names.end(), std::inserter(first_to_last_name_map, first_to_last_name_map.begin()), [](const std::string& i){
if (i == "bad")
return std::pair<std::string, std::string>("bad", "bad"); // Don't Want This
else
return std::pair<std::string, std::string>(i.substr(0,5), i.substr(5,5));
});
where I'm transforming a vector to a map using std::transform with a lambda function. My problem is that sometimes, as shown, I don't want to return anything from my lambda function, i.e. I basically want to skip that i and go to the next one (without adding anything to the map).
Is there any way to achieve what I'm thinking about? I can use boost if it helps. I want to avoid a solution where I have to do a pre-process or post-process on my vector to filter out the "bad" items; I should only need to look at each item once. Also, my actual logic is a bit more complicated than the if/else as written, so I think it would be nice to keep things encapsulated in this std::transform/lambda model if possible (though maybe what I'm trying to achieve isn't possible with this model).
EDIT: Just to emphasize, I'm looking to perform this operation (selectively processing vector elements and inserting them into a map) in the most efficient way possible, even if it means a less elegant solution or a big rewrite. I could even use a different map data type depending on what is most efficient.
template<class Src, class Sink, class F>
void transform_if(Src&& src, Sink&& sink, F&& f){
for(auto&& x:std::forward<Src>(src))
if(auto&& e=f(decltype(x)(x)))
*sink++ = *decltype(e)(e);
}
Now simply get a boost or std or std experiental optional. Have your f return an optional<blah>.
auto sink = std::inserter(first_to_last_name_map, first_to_last_name_map.begin());
using pair_type = decltype(first_to_last_name_map)::value_type;
transform_if(names, sink,
[](const std::string& i)->std::optional<pair_type>{
if (i == "bad")
return {}; // Don't Want This
else
return std::make_pair(i.substr(0,5), i.substr(5,5));
}
);
My personal preferred optional actually has begin end defined. And we get this algorithm:
template<class Src, class Sink, class F>
void polymap(Src&& src, Sink&& sink, F&& f){
for(auto&& x:std::forward<Src>(src))
for(auto&& e:f(decltype(x)(x)))
*sink++ = decltype(e)(e);
}
which now lets the f return a range, where optional is a model of a zero or one element range.
You can simply have a first/last pass with std::remove_if. E.g.
std::vector<std::string> names;
std::map<std::string, std::string> first_to_last_name_map;
std::transform(names.begin(),
std::remove_if(names.begin(),
names.end(),
[](const std::string &str){
return str=="bad";
}),
std::inserter(first_to_last_name_map,
first_to_last_name_map.begin()),
[](const std::string& i){
return std::pair<std::string, std::string>(i.substr(0,5), i.substr(5,5));
});
Note that remove_if simply shifts the removed items past the iterator it returns.
You can use boost::adaptors::filtered to first filter the vector of the elements you don't want, before passing it to transform.
using boost::adaptors::filtered;
boost::transform(names | filtered([](std::string const& s) { return s != "bad"; }),
std::inserter(first_to_last_name_map, first_to_last_name_map.begin()),
[](std::string const& i) { return std::make_pair(i.substr(0,5), i.substr(5,5)); });
Live demo
I have the following code. I'm trying to eliminate the need for explicitly passing the localization_data_t::language_t type into the lambda argument.
auto language_itr = std::find_if(languages.begin(), languages.end(), [&](const localization_data_t::language_t& language)
{
return language.code == language_code;
});
I assume there is a way to do this since the type of the objects to be iterated over can be derived by the compiler via the iterator's underlying type. However, I have found no such example in my travels.
Any help would be appreciated.
You can use decltype in C++11:
auto result = std::find_if(v.begin(), v.end(), [](const decltype(*v.begin())& t) { /* */ });
in C++1y you can just use auto.
auto result = std::find_if(v.begin(), v.end(), [](const auto& t) { /* */ });
There's also std::iterator_traits but it's more verbose.
Coming back to C++ after years of C# I was wondering what the modern - read: C++11 - way of filtering an array would be, i.e. how can we achieve something similar to this Linq query:
var filteredElements = elements.Where(elm => elm.filterProperty == true);
In order to filter a vector of elements (strings for the sake of this question)?
I sincerely hope the old STL style algorithms (or even extensions like boost::filter_iterator) requiring explicit methods to be defined are superseded by now?
See the example from cplusplus.com for std::copy_if:
std::vector<int> foo = {25,15,5,-5,-15};
std::vector<int> bar;
// copy only positive numbers:
std::copy_if (foo.begin(), foo.end(), std::back_inserter(bar), [](int i){return i>=0;} );
std::copy_if evaluates the lambda expression for every element in foo here and if it returns true it copies the value to bar.
The std::back_inserter allows us to actually insert new elements at the end of bar (using push_back()) with an iterator without having to resize it to the required size first.
In C++20, use filter view from the ranges library: (requires #include <ranges>)
// namespace views = std::ranges::views;
vec | views::filter([](int a){ return a % 2 == 0; })
lazily returns the even elements in vec.
(See [range.adaptor.object]/4 and [range.filter])
This is already supported by GCC 10 (live demo). For Clang and older versions of GCC, the original range-v3 library can be used too, with #include <range/v3/view/filter.hpp> (or #include <range/v3/all.hpp>) and the ranges::views namespace instead of std::ranges::views (live demo).
A more efficient approach, if you don't actually need a new copy of the list, is remove_if, which actually removes the elements from the original container.
I think Boost.Range deserves a mention too. The resulting code is pretty close to the original:
#include <boost/range/adaptors.hpp>
// ...
using boost::adaptors::filtered;
auto filteredElements = elements | filtered([](decltype(elements)::value_type const& elm)
{ return elm.filterProperty == true; });
The only downside is having to explicitly declare the lambda's parameter type. I used decltype(elements)::value_type because it avoids having to spell out the exact type, and also adds a grain of genericity. Alternatively, with C++14's polymorphic lambdas, the type could be simply specified as auto:
auto filteredElements = elements | filtered([](auto const& elm)
{ return elm.filterProperty == true; });
filteredElements would be a range, suitable for traversal, but it's basically a view of the original container. If what you need is another container filled with copies of the elements satisfying the criteria (so that it's independent from the lifetime of the original container), it could look like:
using std::back_inserter; using boost::copy; using boost::adaptors::filtered;
decltype(elements) filteredElements;
copy(elements | filtered([](decltype(elements)::value_type const& elm)
{ return elm.filterProperty == true; }), back_inserter(filteredElements));
Improved pjm code following underscore-d suggestions:
template <typename Cont, typename Pred>
Cont filter(const Cont &container, Pred predicate) {
Cont result;
std::copy_if(container.begin(), container.end(), std::back_inserter(result), predicate);
return result;
}
Usage:
std::vector<int> myVec = {1,4,7,8,9,0};
auto filteredVec = filter(myVec, [](int a) { return a > 5; });
My suggestion for C++ equivalent of C#
var filteredElements = elements.Where(elm => elm.filterProperty == true);
Define a template function to which you pass a lambda predicate to do the filtering. The template function returns the filtered result. eg:
template<typename T>
vector<T> select_T(const vector<T>& inVec, function<bool(const T&)> predicate)
{
vector<T> result;
copy_if(inVec.begin(), inVec.end(), back_inserter(result), predicate);
return result;
}
to use - giving a trivial examples:
std::vector<int> mVec = {1,4,7,8,9,0};
// filter out values > 5
auto gtFive = select_T<int>(mVec, [](auto a) {return (a > 5); });
// or > target
int target = 5;
auto gt = select_T<int>(mVec, [target](auto a) {return (a > target); });
Suppose I have the following two data structures:
std::vector<int> all_items;
std::set<int> bad_items;
The all_items vector contains all known items and the bad_items vector contains a list of bad items. These two data structures are populated entirely independent of one another.
What's the proper way to write a method that will return a std::vector<int> contain all elements of all_items not in bad_items?
Currently, I have a clunky solution that I think can be done more concisely. My understanding of STL function adapters is lacking. Hence the question. My current solution is:
struct is_item_bad {
std::set<int> const* bad_items;
bool operator() (int const i) const {
return bad_items.count(i) > 0;
}
};
std::vector<int> items() const {
is_item_bad iib = { &bad_items; };
std::vector<int> good_items(all_items.size());
std::remove_copy_if(all_items.begin(), all_items.end(),
good_items.begin(), is_item_bad);
return good_items;
}
Assume all_items, bad_items, is_item_bad and items() are all a part of some containing class. Is there a way to write them items() getter such that:
It doesn't need temporary variables in the method?
It doesn't need the custom functor, struct is_item_bad?
I had hoped to just use the count method on std::set as a functor, but I haven't been able to divine the right way to express that w/ the remove_copy_if algorithm.
EDIT: Fixed the logic error in items(). The actual code didn't have the problem, it was a transcription error.
EDIT: I have accepted a solution that doesn't use std::set_difference since it is more general and will work even if the std::vector isn't sorted. I chose to use the C++0x lambda expression syntax in my code. My final items() method looks like this:
std::vector<int> items() const {
std::vector<int> good_items;
good_items.reserve(all_items.size());
std::remove_copy_if(all_items.begin(), all_items.end(),
std::back_inserter(good_items),
[&bad_items] (int const i) {
return bad_items.count(i) == 1;
});
}
On a vector of about 8 million items the above method runs in 3.1s. I bench marked the std::set_difference approach and it ran in approximately 2.1s. Thanks to everyone who supplied great answers.
As jeffamaphone suggested, if you can sort any input vectors, you can use std::set_difference which is efficient and less code:
#include <algorithm>
#include <set>
#include <vector>
std::vector<int>
get_good_items( std::vector<int> const & all_items,
std::set<int> const & bad_items )
{
std::vector<int> good_items;
// Assumes all_items is sorted.
std::set_difference( all_items.begin(),
all_items.end(),
bad_items.begin(),
bad_items.end(),
std::back_inserter( good_items ) );
return good_items;
}
Since your function is going to return a vector, you will have to make a new vector (i.e. copy elements) in any case. In which case, std::remove_copy_if is fine, but you should use it correctly:
#include <iostream>
#include <vector>
#include <set>
#include <iterator>
#include <algorithm>
#include <functional>
std::vector<int> filter(const std::vector<int>& all, const std::set<int>& bad)
{
std::vector<int> result;
remove_copy_if(all.begin(), all.end(), back_inserter(result),
[&bad](int i){return bad.count(i)==1;});
return result;
}
int main()
{
std::vector<int> all_items = {4,5,2,3,4,8,7,56,4,2,2,2,3};
std::set<int> bad_items = {2,8,4};
std::vector<int> filtered_items = filter(all_items, bad_items);
copy(filtered_items.begin(), filtered_items.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
}
To do this in C++98, I guess you could use mem_fun_ref and bind1st to turn set::count into a functor in-line, but there are issues with that (which resulted in deprecation of bind1st in C++0x) which means depending on your compiler, you might end up using std::tr1::bind anyway:
remove_copy_if(all.begin(), all.end(), back_inserter(result),
bind(&std::set<int>::count, bad, std::tr1::placeholders::_1)); // or std::placeholders in C++0x
and in any case, an explicit function object would be more readable, I think:
struct IsMemberOf {
const std::set<int>& bad;
IsMemberOf(const std::set<int>& b) : bad(b) {}
bool operator()(int i) const { return bad.count(i)==1;}
};
std::vector<int> filter(const std::vector<int>& all, const std::set<int>& bad)
{
std::vector<int> result;
remove_copy_if(all.begin(), all.end(), back_inserter(result), IsMemberOf(bad));
return result;
}
At the risk of appearing archaic:
std::set<int> badItems;
std::vector<int> items;
std::vector<int> goodItems;
for ( std::vector<int>::iterator iter = items.begin();
iter != items.end();
++iter)
{
int& item = *iter;
if ( badItems.find(item) == badItems.end() )
{
goodItems.push_back(item);
}
}
std::remove_copy_if returns an iterator to the target collection. In this case, it would return good_items.end() (or something similar). good_items goes out of scope at the end of the method, so this would cause some memory errors. You should return good_items or pass in a new vector<int> by reference and then clear, resize, and populate it. This would get rid of the temporary variable.
I believe you have to define the custom functor because the method depends on the object bad_items which you couldn't specify without it getting hackey AFAIK.