I have a pretty simple flatmap
function implemented in C++ for std::vector
, but it has been suggested that ranges are generally better. Here's the vector based solution:
// flatmap: [A] -> (A->[B]) -> [B]
template<typename T, typename FN>
static auto flatmap(const std::vector<T> &vec, FN fn)
-> std::vector<typename std::remove_reference<decltype(fn(T())[0])>::type> {
std::vector<typename std::remove_reference<decltype(fn(T())[0])>::type> result;
for(auto x : vec) {
auto y = fn(x);
for( auto v : y ) {
result.push_back(v);
}
}
return result;
};
It has also been suggested that I use iterators, but that breaks the nice composability of the function:
map(filter(flatmap( V, fn), fn2), fn3)
I would assume that in a range-v3 world I'd be aiming for writing the above as:
auto result = v | flatmap(fn) | filter(fn2) | transform(fn3);
It feels like flatmap
should just be a trivial combination of views::for_each
, yield_from
and transform
, but I'm struggling to work out how to hook them all together.
IIUC, your flatmap
function is nothing but range-v3's view::for_each
. Try:
using namespace ranges;
auto result = v | view::for_each(fn) | to_vector;
HTH
If I understood correctly, what your function flatmap
must do, you can write it as v | view::transform(fn) | action::join
. Here is example of making it with ranges:
#include <range/v3/all.hpp>
#include <iostream>
#include <string>
#include <utility>
#include <vector>
// flatmap: [A] -> (A->[B]) -> [B]
template<typename T, typename FN>
static auto flatmap(const std::vector<T> &vec, FN fn)
-> std::vector<typename std::remove_reference<decltype(fn(T())[0])>::type> {
std::vector<typename std::remove_reference<decltype(fn(T())[0])>::type> result;
for(auto x : vec) {
auto y = fn(x);
for( auto v : y ) {
result.push_back(v);
}
}
return result;
};
// This will be test function for both flatmap and range usage
std::vector<std::string> testFn(int n)
{
std::vector<std::string> result;
int ofs = 0;
for(int i = 0; i < n; ++i)
{
char initialChar = 'A' + ofs;
std::string partialResult = "\"";
for(int j = 0; j <=i; ++j, ++ofs)
{
partialResult.append(1, initialChar+j);
}
partialResult += "\"";
result.push_back(partialResult);
}
return std::move(result);
}
int main(int, char**)
{
std::vector<int> vv {1, 2, 3, 4, 5, 6};
// test flatmap
auto r2 = flatmap(vv, testFn);
for(auto s:r2)
{
std::cout << s << " " ;
}
std::cout << "\n";
using namespace ranges;
// test ranges equivalent
auto rng = vv|view::transform(testFn)|action::join;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this is an equivalent for flatmap in ranges terms
for(auto s:rng)
{
std::cout << s << " ";
}
std::cout << "\n";
std::cout << std::flush;
return 0;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With