I have a string vector like {"1.2","3.4","0.5","200.7"}
.
I would like to convert each element into double and store it in a vector<double>
.
Like so {1.2,3.4,0.5,200.7}
What would be the best way to do this?
I know of the std::stod(string, size)
; But I am hoping for a better way to do this.
I was looking for something like:
vector<double> doubleVector = convertStringVectortoDoubleVector(myStringVector);
There doesn't seem to be anything like that; so what is the next best thing?
EDIT: Here's what I ended up using:
std::vector<double> convertStringVectortoDoubleVector(const std::vector<std::string>& stringVector){
std::vector<double> doubleVector(stringVector.size());
std::transform(stringVector.begin(), stringVector.end(), doubleVector.begin(), [](const std::string& val)
{
return stod(val);
});
return doubleVector;}
For a complete answer check out zac howland's answer and Chris Jester-Young's answer. (P.S. This is based entirely on Zac's answer) Thanks
For completeness (since Chris removed the edit from his answer):
std::vector<double> doubleVector(stringVector.size());
std::transform(stringVector.begin(), stringVector.end(), doubleVector.begin(), [](const std::string& val)
{
return std::stod(val);
});
Comparison to using std::back_inserter
without reserve
Without reserve, you run the risk of having to resize the array each time the back_inserter
calls push_back
. It has to check the current size against the current capacity, and if the capacity needs to be increased, it will copy the vector to a new location (with increased capacity). After all of that, it will increase the size and insert the new element. This is a lot of overhead when you know what the size should be to start with (it will match the size of the stringVector
).
Comparision to using std::back_inserter
with reserve
Reserving the proper amount of memory will prevent the reallocation problem, but push_back
still updates the size and does the check to see if the capacity has been reached each iteration. You've reduced the overhead a lot (no longer running the risk of having to "move" the array because of sizing issues), but you still have a lot of unneeded conditional checks.
Setting the size initially has a small overhead of setting all the elements to a default value (0.0 in the case of doubles). With each iteration, you are then simply setting the value of the current element. So for a vector of N elements, you have 2N + 2 assignments (setting the capacity, size, the initial value of the elements, and the real value of the elements) with no unneeded conditional checks. The reserve method has 2N + 1 assignments (set the capacity once, update the size N times, and set the value of N doubles) in addition to N conditional checks.
If you really wanted to optimize it even further, you could create your own iterator wrapper that does the conversion, which would then allow you to write the correct value for the doubles when you initialize the vector:
// pseudo-code
std::vector<double> doubleVector(my_string_conversion_iterator(stringVector.begin()), my_string_conversion_iterator(stringVector.end());
You should use std::transform
to apply the conversion to every element.
vector<double> doubleVector;
doubleVector.reserve(stringVector.size());
transform(stringVector.begin(), stringVector.end(), back_inserter(doubleVector),
[](string const& val) {return stod(val);});
As Zac Howland points out, here's another approach to this, which involves initialising a vector with default-constructed elements first, and then simply filling the vector with the correct values afterwards:
vector<double> doubleVector(stringVector.size());
transform(stringVector.begin(), stringVector.end(), doubleVector.begin(),
[](string const& val) {return stod(val);});
The advantage of this approach is that the vector is sized exactly once, rather than continuously growing. The disadvantage is that vector elements have to be default-constructed first, and then be reassigned with the correct value afterwards. This tradeoff is worth it for element types that satisfy all of the following:
In this instance, double
fulfils all four requirements, and so the latter approach is better. For other types in general, and in particular when writing a function template to do this, your default implementation should use the former approach.
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