28

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?

Trojan
  • 2,256
  • 28
  • 40
Bogdan
  • 662
  • 1
  • 12
  • 23

5 Answers5

71

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.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 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. – Bogdan Dec 31 '09 at 16:40
  • 1
    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. – John Dibling Dec 31 '09 at 17:44
  • 3
    Btw, this approach possibly scales very badly since there might be a lof of copying / reallicating involved. – sellibitze Sep 13 '10 at 18:19
  • 1
    `std::reduce` (C++17) - more faster than `std::accumulate`: `std::string a = std::reduce(vec.begin(), vec.end(), std::string(""));` – Kuznetsov-M Feb 03 '23 at 11:58
17

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;
Davide Spataro
  • 7,319
  • 1
  • 24
  • 36
Sanjaya R
  • 6,246
  • 2
  • 17
  • 19
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.

pixelgrease
  • 1,940
  • 23
  • 26
  • 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. – Alexis Wilke Jul 12 '16 at 22:03
  • 3
    @pixelgrease, what is the cat function? how is it defined? thanks – mrchance Aug 09 '20 at 17:05
8

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;
  }
Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345
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(", "));
}
Eduardo Reis
  • 1,691
  • 1
  • 22
  • 45