Combining a vector of strings
Asked Answered
V

5

29

I've been reading Accelerated C++ and I have to say it's an interesting book.

In chapter 6, I have to use a function from <algorithm> to concatenate from a vector<string> into a single string. I could use accumulate, but it doesn't help because string containers can only push_back characters.

int main () {
  using namespace std;
  string str = "Hello, world!";
  vector<string>  vec (10, str);
  // Concatenate here?

  return 0;
}

How do I join the strings together?

Valor answered 31/12, 2009 at 16:16 Comment(1)
Please consider changing the accepted solution to something less inefficient.Nystagmus
M
72

Assuming this is question 6.8, it doesn't say you have to use accumulate - it says use "a library algorithm". However, you can use accumulate:

#include <numeric>
    
int main () {
    std::string str = "Hello World!";
    std::vector<std::string> vec(10,str);
    std::string a = std::accumulate(vec.begin(), vec.end(), std::string(""));
    std::cout << a << std::endl;
}

All that accumulate does is set 'sum' to the third parameter, and then for all of the values 'val' from first parameter to second parameter, do:

sum = sum + val

it then returns 'sum'. Despite the fact that accumulate is declared in <numeric> it will work for anything that implements operator+()


Note: This solution, while elegant, is inefficient, as a new string will be allocated and populated for each element of vec.

Monometallism answered 31/12, 2009 at 16:35 Comment(4)
Thanks, I tried using accumulate with the 3rd parameter as a.begin, didn't work, also I tried with back_inserter which failed too. Can you explain how this works? Thanks alot.Valor
It takes each element in the vector from .begin() to .end() and accumulates them in to the third parameter, which is an empty std::string passed in as a temporary. std::acumulate()'s return value is the result of the accumulation, passed by value.Avesta
Btw, this approach possibly scales very badly since there might be a lof of copying / reallicating involved.Preposterous
std::reduce (C++17) - more faster than std::accumulate: std::string a = std::reduce(vec.begin(), vec.end(), std::string(""));Ludly
B
19

How about std::copy?

std::ostringstream os;
std::copy( vec_strings.begin(), vec_string.end(), std::ostream_iterator<std::string>( os ) );
std::cout << os.str() << std::endl;
Brunel answered 31/12, 2009 at 19:15 Comment(2)
Can you compare this solution to the accepted one?Nystagmus
accepted answer is O(N^2): #15347623Lodicule
B
13

The following snippet compiles in Visual C++ 2012 and uses a lambda function:

int main () {
    string str = "Hello World!";
    vector<string>  vec (10,str);

    stringstream ss;
    for_each(vec.begin(), vec.end(), [&ss] (const string& s) { cat(ss, s); });

    cout << ss.str() << endl;
}

The accumulate example in the 1st answer is elegant, but as sellibitze pointed out, it reallocates with each concatenation and scales at O(N²). This for_each snippet scales at about O(N). I profiled both solutions with 100K strings; the accumulate example took 23.6 secs, but this for_each snippet took 0.054 sec.

Bibliology answered 16/4, 2013 at 6:12 Comment(2)
It could be even faster to create an std::sring call reserve(final-size), then just use += which should be fast since the buffer is already of the right size.Ofeliaofella
@pixelgrease, what is the cat function? how is it defined? thanksHeptarchy
P
7

I am not sure about your question.Where lies the problem? Its just a matter of a loop.

#include<vector>
#include<string>
#include<iostream>

int main () 
{
    std::string str = "Hello World!";
    std::vector<string>  vec (10,str);

    for(size_t i=0;i!=vec.size();++i)
        str=str+vec[i];
    std::cout<<str;
}

EDIT :

Use for_each() from <algorithm>

Try this:

#include<vector>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
string i;
void func(string &k)
{
  i+=k;
}
int main () {
    string str = "Hello World!";
    vector<string>  vec (10,str);

    for_each(vec.begin(),vec.end(),func);
    cout<<i;
    return 0;
  }
Pyrethrin answered 31/12, 2009 at 16:22 Comment(2)
I have to use a function from algorithm header to do this.Valor
Or use a proper functor With for_each instead of a free function. Saves you having a global variableAdscription
P
0

This answer is an improved version of the one from @PrasoonSaurav by adding a helpful sep argument.

inline std::string joinStrings(std::vector<std::string> arr, std::string sep) {
    std::string out = arr[0];
    for(unsigned int i = 1; i < arr.size(); i++) {
        out += sep + arr[i];
    }
    return out;
}

// Overload function parameter to add default value for the separator
inline std::string joinStrings(std::vector<std::string> arr) {
    return joinStrings(arr, std::string(", "));
}
Pence answered 16/3, 2022 at 20:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.