How to build std :: string from std :: vector <string>?
I would like to build std::string from std::vector<std::string> .
I could use std::stringsteam , but imagine a shorter path exists:
std::string string_from_vector(const std::vector<std::string> &pieces) { std::stringstream ss; for(std::vector<std::string>::const_iterator itr = pieces.begin(); itr != pieces.end(); ++itr) { ss << *itr; } return ss.str(); } How else can I do this?
You can use the standard function std::accumulate() from the <numeric> header (it works because operator + overloading is defined for string , which returns the concatenation of its two arguments):
#include <vector> #include <string> #include <numeric> #include <iostream> int main() { std::vector<std::string> v{"Hello, ", " Cruel ", "World!"}; std::string s; s = accumulate(begin(v), end(v), s); std::cout << s; // Will print "Hello, Cruel World!" } Alternatively, you can use a more efficient small for loop:
#include <vector> #include <string> #include <iostream> int main() { std::vector<std::string> v{"Hello, ", "Cruel ", "World!"}; std::string result; for (auto const& s : v) { result += s; } std::cout << result; // Will print "Hello, Cruel World!" } 
C ++ 03
std::string s; for (std::vector<std::string>::const_iterator i = v.begin(); i != v.end(); ++i) s += *i; return s; C ++ 11 (a subset of MSVC 2010)
std::string s; std::for_each(v.begin(), v.end(), [&](const std::string &piece){ s += piece; }); return s; C ++ 11
std::string s; for (const auto &piece : v) s += piece; return s; Do not use std::accumulate to concatenate strings , this is the classic Schlemel-PΓ€nter algorithm , even worse than the usual example using strcat in C. Without C ++ 11 movement semantics, it causes two unnecessary battery copies for each element of the vector. Even with the semantics of movement, it still contains one unnecessary copy of the battery for each cell.
The three examples above are O (n) .
std::accumulate - O (nΒ²) for strings.
You can do
std::accumulateO (n) for strings by providing a custom functor:std::string s = std::accumulate(v.begin(), v.end(), std::string{}, [](std::string &s, const std::string &piece) -> decltype(auto) { return s += piece; });Note that
smust be a reference to non-constant, the lambda type of the return value must be a reference (hencedecltype(auto)), and the body must use+=not+.
C ++ 20
In the current project of what is expected to become C ++ 20, the definition of std::accumulate been changed to use std::move when added to the battery, so starting from C ++ 20 onwards, accumulate will be O (n ) for strings, and can be used as a single line:
std::string s = std::accumulate(v.begin(), v.end(), std::string{}); Why not just use the + operator to add them?
std::string string_from_vector(const std::vector<std::string> &pieces) { return std::accumulate(pieces.begin(), pieces.end(), std::string("")); } std :: accumulate uses std :: plus under the hood by default, and adding two lines is a concatenation in C ++, since the + operator is overloaded for std :: string.
My personal choice would be a range-based cycle, as in Oktalist's answer .
Boost also offers a good solution:
#include <boost/algorithm/string/join.hpp> #include <iostream> #include <vector> int main() { std::vector<std::string> v{"first", "second"}; std::string joined = boost::algorithm::join(v, ", "); std::cout << joined << std::endl; } Fingerprints:
first second
In any case, I find the std::accumulate() approach to be misused by this algorithm (regardless of complexity).
A little late to the party, but I liked that we could use initializer lists:
std::string join(std::initializer_list<std::string> i) { std::vector<std::string> v(i); std::string res; for (const auto &s: v) res += s; return res; } Then you can just call (Python style):
join({"Hello", "World", "1"}) Google Abseil has an absl :: StrJoin function that does what you need.
An example from the header file. Please note that the delimiter may also be ""
// std::vector<std::string> v = {"foo", "bar", "baz"}; // std::string s = absl::StrJoin(v, "-"); // EXPECT_EQ("foo-bar-baz", s); With C ++ 11, the stringstream method is not too scary:
#include <vector> #include <string> #include <algorithm> #include <sstream> #include <iostream> int main() { std::vector<std::string> v{"Hello, ", " Cruel ", "World!"}; std::stringstream s; std::for_each(begin(v), end(v), [&s](const std::string &elem) { s << elem; } ); std::cout << s.str(); } If no trailing spaces are required, use the accumulate defined in <numeric> with a non-standard connection lambda.
#include <iostream> #include <numeric> #include <vector> using namespace std; int main() { vector<string> v; string s; v.push_back(string("fee")); v.push_back(string("fi")); v.push_back(string("foe")); v.push_back(string("fum")); s = accumulate(begin(v), end(v), string(), [](string lhs, const string &rhs) { return lhs.empty() ? rhs : lhs + ' ' + rhs; } ); cout << s << endl; return 0; }