Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert a std::vector<boost::optional<double>> to a std::vector<double>

Tags:

c++

I have a std::vector<boost::optional<double>>, foo say. In this particular instance I need a std::vector<double> where any "optional" element in the other vector maps to a 0 in the new one.

Am I missing a one-line solution for this?

The other choice is the unsatisfactory

std::vector<double> out(foo.size());
for (auto& it : foo){
    out.push_back(it ? *it : 0.0);
}

I'd welcome a solution based on std::optional, even though I don't use that standard yet.

like image 327
Sebastian John Howard Avatar asked Apr 30 '18 08:04

Sebastian John Howard


2 Answers

std::transform solution:

std::vector<double> out(foo.size());
std::transform(foo.begin(), foo.end(), out.begin(), [](const auto& opt){ return opt.value_or(0.0); });

Edit: Added the out definition.

like image 102
Max Langhof Avatar answered Nov 04 '22 13:11

Max Langhof


Here's a solution that constructs the output vector with the desired values. Still can't be forced onto one line though.

auto valueOrZero = [](auto opt){ return opt?*opt:0.0; };
std::vector<double> out(boost::make_transform_iterator(std::begin(foo), valueOrZero), boost::make_transform_iterator(std::end(foo), valueOrZero));

Unfortunately, boost::transform_iterator requires the unary transformation function to be specified for the end iterator, and you can't just repeat the lamdba definition because it also requires both iterators to have exactly the same type. This forces the lambda onto its own line.

I think it would be possible to write a transform iterator that works around this, but you'd have to do it from scratch.

like image 45
BoBTFish Avatar answered Nov 04 '22 13:11

BoBTFish