Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert vector<std::string> to vector<double>

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

like image 621
Shayan RC Avatar asked Nov 28 '13 04:11

Shayan RC


2 Answers

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());
like image 168
Zac Howland Avatar answered Sep 22 '22 04:09

Zac Howland


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:

  1. can be default-constructed
  2. are cheap to default-construct
  3. can be assigned with a value of the same type
  4. are cheap to assign

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.

like image 24
Chris Jester-Young Avatar answered Sep 22 '22 04:09

Chris Jester-Young