7

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

Shayan RC
  • 3,152
  • 5
  • 33
  • 40
  • you can't convert all elements without iterate all elements. if you want something do the conversion lazily, thats different question – Bryan Chen Nov 28 '13 at 04:18
  • 1
    AFAIK, there's no such thing that _"do this without iterating through the entire vector applying it to every element"_ . You can write a function `convertVectortoDouble` though – P0W Nov 28 '13 at 04:18
  • Wouldn't `convertVectortoDouble` have to iterate through the vector though? – David G Nov 28 '13 at 04:19
  • I am looking for an easy way to do it. I know the entire vector must be iterated. I just dont want to do it myself... :) – Shayan RC Nov 28 '13 at 04:23
  • 1
    ...because avoiding three lines of code's worth a half-hour-plus diversion on stackoverflow... – Tony Delroy Nov 28 '13 at 04:41
  • @Tony It gives you the opportunity to learn to do an old thing in a new way. So, yes, I guess it is worth it. Plus it makes your code look cleaner. – Shayan RC Nov 28 '13 at 04:48
  • 1
    If you wrote you own function called `convertVectortoDouble` wouldn't that also make your code look cleaner? I'm all for learning new things but often the simple methods are the best. – john Nov 28 '13 at 06:26
  • 1
    @ShayanRC Re your edit, don't take the `stringVector` by value, instead prefer to take it by const reference. Otherwise, all this talk about performance is just wasted. (You do have to return `doubleVector` by value though, so the return type is correct.) – C. K. Young Dec 02 '13 at 04:54
  • How to do this without lambda experession, as the g++ compiler complains _only available with -std=c++11_ – droid192 Feb 15 '16 at 20:23

4 Answers4

14

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());
Zac Howland
  • 15,777
  • 1
  • 26
  • 42
  • Yay for completeness (+1). My answer extends that by explaining the circumstances under which you should prefer each approach. In particular for generic functions where the target type is not default-constructible and/or assignable, this approach would be infeasible. – C. K. Young Nov 28 '13 at 20:03
  • Also re the custom iterator approach, unless it models a random access iterator (in particular, `distance` should run in constant time), the performance is unlikely to be better than the `back_inserter` approach. – C. K. Young Nov 28 '13 at 20:07
  • Well, in order to be inserted into a vector, the type would have to be assignable, but in general, I agree. The custom iterator approach would only be more efficient if the wrapper was written as a thin wrapper over the string vector's own iterator (and simply modified the dereference operator to return a double instead of a string). It allows you to merge the benefits of the non-`back_inserter` and `back_inserter` approaches. – Zac Howland Nov 29 '13 at 14:26
  • 2
    Side note: The boost `transform_iterator` is very similar to what would be done for this type of custom iterator. – Zac Howland Nov 29 '13 at 15:44
9

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.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • 1
    If you set the size of the double vector to the size of the string vector, you can avoid using the `back_inserter` – Zac Howland Nov 28 '13 at 05:12
  • 1
    @ZacHowland Indeed, and the way to do so (in terms of avoiding reallocation, rather than avoiding `back_inserter`) is to use `vector::reserve`. :-P (I have a strong distaste for default-constructing objects and then assigning them later on, far rather preferring to construct the correct value in the first place, so yes, I kicked back your edit.) – C. K. Young Nov 28 '13 at 06:07
  • Except if you are looking to gain performance (not really an issue with this particular problem), `back_inserter` is calling `push_back` each time, which means it is updating the `size` member each time (and checking the `capacity` each time). Using the default size constructor sets the `size` member once, at the cost of a very cheap default initialization for your vector of doubles. In short, you are trading a lot of CPU time simply to avoid setting the value of your doubles twice. – Zac Howland Nov 28 '13 at 06:41
  • @ZacHowland True, there's a time and a place where this extra update/check of the size has a higher cost than the extra initialisation and assignment. Let me update my answer with a few more nuances. – C. K. Young Nov 28 '13 at 19:07
2

Use std::transform

vector<string> str ;
vector<double> dv ;

std::transform(str.begin(), str.end(), back_inserter(dv), [](const string & astr){ return stod( astr) ; } ) ;
woolstar
  • 5,063
  • 20
  • 31
1

you can use std:for_each and lambda in C++11.

vector<string> a = {"1.2","3.4","0.5","200.7"};
vector<double> b;
for_each(a.begin(), a.end(), [&b](const string &ele) { b.push_back(stod(ele)); });
Romano Zumbé
  • 7,893
  • 4
  • 33
  • 55
BlackMamba
  • 10,054
  • 7
  • 44
  • 67