Convert a vector to a string
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}
I have a vector<int>
container that has integers (e.g. {1,2,3,4}) and I would like to convert to a string of the form
"1,2,3,4"
What is the cleanest way to do that in C++?
In Python this is how I would do it:
>>> array = [1,2,3,4]
>>> ",".join(map(str,array))
'1,2,3,4'
c++ vector tostring
add a comment |
I have a vector<int>
container that has integers (e.g. {1,2,3,4}) and I would like to convert to a string of the form
"1,2,3,4"
What is the cleanest way to do that in C++?
In Python this is how I would do it:
>>> array = [1,2,3,4]
>>> ",".join(map(str,array))
'1,2,3,4'
c++ vector tostring
1
Closely related: stackoverflow.com/questions/4850473/…
– Ciro Santilli 新疆改造中心996ICU六四事件
Mar 8 '15 at 21:39
add a comment |
I have a vector<int>
container that has integers (e.g. {1,2,3,4}) and I would like to convert to a string of the form
"1,2,3,4"
What is the cleanest way to do that in C++?
In Python this is how I would do it:
>>> array = [1,2,3,4]
>>> ",".join(map(str,array))
'1,2,3,4'
c++ vector tostring
I have a vector<int>
container that has integers (e.g. {1,2,3,4}) and I would like to convert to a string of the form
"1,2,3,4"
What is the cleanest way to do that in C++?
In Python this is how I would do it:
>>> array = [1,2,3,4]
>>> ",".join(map(str,array))
'1,2,3,4'
c++ vector tostring
c++ vector tostring
edited May 17 '18 at 19:54
jww
54.6k42240523
54.6k42240523
asked Sep 16 '09 at 3:14
dzhelildzhelil
8,9772391139
8,9772391139
1
Closely related: stackoverflow.com/questions/4850473/…
– Ciro Santilli 新疆改造中心996ICU六四事件
Mar 8 '15 at 21:39
add a comment |
1
Closely related: stackoverflow.com/questions/4850473/…
– Ciro Santilli 新疆改造中心996ICU六四事件
Mar 8 '15 at 21:39
1
1
Closely related: stackoverflow.com/questions/4850473/…
– Ciro Santilli 新疆改造中心996ICU六四事件
Mar 8 '15 at 21:39
Closely related: stackoverflow.com/questions/4850473/…
– Ciro Santilli 新疆改造中心996ICU六四事件
Mar 8 '15 at 21:39
add a comment |
19 Answers
19
active
oldest
votes
Definitely not as elegant as Python, but nothing quite is as elegant as Python in C++.
You could use a stringstream
...
std::stringstream ss;
for(size_t i = 0; i < v.size(); ++i)
{
if(i != 0)
ss << ",";
ss << v[i];
}
std::string s = ss.str();
You could also make use of std::for_each
instead.
I think you mean array.size() not v.size(), no?
– Mark Elliot
Sep 16 '09 at 3:35
1
ya whatever the vector is called.
– Brian R. Bondy
Sep 16 '09 at 3:37
2
That should bestd::string s = ss.str()
. If you want aconst char*
, uses.c_str()
. (Note that, while syntactically correct,ss.str().c_str()
will give you aconst char*
that points to a temporary which will will cease to exist at the end of the full expression. That hurts.)
– sbi
Sep 16 '09 at 8:56
1
why not just use string.append?
– Baiyan Huang
Jun 27 '14 at 9:56
11
answer is incomplete without#include <sstream>
– renadeen
Mar 20 '16 at 20:02
|
show 2 more comments
Using std::copy and std::ostream_iterator we can get something as elegant as python.
#include <iostream>
#include <sstream>
#include <algorithm>
#include <iterator>
int main()
{
int array = {1,2,3,4};
std::copy(array, array+4, std::ostream_iterator<int>(std::cout,","));
}
See this question for a little class I wrote. This will not print the trailing comma. Also if we assume that C++14 will continue to give us range based equivalents of algorithms like this:
namespace std {
// I am assuming something like this in the C++14 standard
// I have no idea if this is correct but it should be trivial to write if it does not appear.
template<typename C, typename I>
void copy(C const& container, I outputIter) {copy(begin(container), end(container), outputIter);}
}
using POI = PrefexOutputIterator;
int main()
{
int array = {1,2,3,4};
std::copy(array, POI(std::cout, ","));
// ",".join(map(str,array)) // closer
}
11
I think this is not quite equivalent to Python's join - it will insert an extra "," at the end.
– 1800 INFORMATION
Sep 16 '09 at 4:13
2
Not equivalent but just as elegant (in fact I think more so but that is just an opinion).
– Martin York
Sep 16 '09 at 4:36
19
Obviously elegance is subjective. So if you and two other people prefer longer, more repetitive code that doesn't work, then it's more elegant ;-p
– Steve Jessop
Sep 16 '09 at 10:19
Why doesn't it work?
– jalf
Sep 16 '09 at 12:31
1
You can ignore the final comma by using the string::substr member function. Assign the the substring from 0 to n-1 to your result variable.
– Dan
Sep 16 '09 at 15:59
|
show 2 more comments
Another alternative is the use of std::copy
and the ostream_iterator
class:
#include <iterator> // ostream_iterator
#include <sstream> // ostringstream
#include <algorithm> // copy
std::ostringstream stream;
std::copy(array.begin(), array.end(), std::ostream_iterator<>(stream));
std::string s=stream.str();
s.erase(s.length()-1);
Also not as nice as Python.
For this purpose, I created a join
function:
template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
T result;
for (A it=begin;
it!=end;
it++)
{
if (!result.empty())
result.append(t);
result.append(*it);
}
return result;
}
Then used it like this:
std::string s=join(array.begin(), array.end(), std::string(","));
You might ask why I passed in the iterators. Well, actually I wanted to reverse the array, so I used it like this:
std::string s=join(array.rbegin(), array.rend(), std::string(","));
Ideally, I would like to template out to the point where it can infer the char type, and use string-streams, but I couldn't figure that out yet.
Since it would be too much for a comment, I have posted an answer (stackoverflow.com/questions/1430757/1432040#1432040) which attempts to solve the riddle given in your last sentence.
– sbi
Sep 16 '09 at 9:51
Yourjoin
function can be used with vectors as well? May you please give example, I'm new to C++.
– Noitidart
Jan 15 '17 at 1:09
Can you change the iterator to a preincrement in the answer?
– Millie Smith
Sep 8 '17 at 3:12
add a comment |
You can use std::accumulate. Consider the following example
if (v.empty()
return std::string();
std::string s = std::accumulate(v.begin()+1, v.end(), std::to_string(v[0]),
(const std::string& a, int b){
return a + ',' + std::to_string(b);
});
add a comment |
With Boost and C++11 this could be achieved like this:
auto array = {1,2,3,4};
join(array | transformed(tostr), ",");
Well, almost. Here's the full example:
#include <array>
#include <iostream>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>
int main() {
using boost::algorithm::join;
using boost::adaptors::transformed;
auto tostr = static_cast<std::string(*)(int)>(std::to_string);
auto array = {1,2,3,4};
std::cout << join(array | transformed(tostr), ",") << std::endl;
return 0;
}
Credit to Praetorian.
You can handle any value type like this:
template<class Container>
std::string join(Container const & container, std::string delimiter) {
using boost::algorithm::join;
using boost::adaptors::transformed;
using value_type = typename Container::value_type;
auto tostr = static_cast<std::string(*)(value_type)>(std::to_string);
return join(container | transformed(tostr), delimiter);
};
add a comment |
This is just an attempt to solve the riddle given by 1800 INFORMATION's remark on his second solution lacking genericity, not an attempt to answer the question:
template <class Str, class It>
Str join(It begin, const It end, const Str &sep)
{
typedef typename Str::value_type char_type;
typedef typename Str::traits_type traits_type;
typedef typename Str::allocator_type allocator_type;
typedef std::basic_ostringstream<char_type,traits_type,allocator_type>
ostringstream_type;
ostringstream_type result;
if(begin!=end)
result << *begin++;
while(begin!=end) {
result << sep;
result << *begin++;
}
return result.str();
}
Works On My Machine(TM).
Visual Studio 2013 gets very confused by the typedefs. Not that you could have known that in 2009.
– Grault
Apr 9 '15 at 4:01
@Jes: I have now been staring at this for 5mins, but couldn't figure out what VS might trip over. Can you be more specific?
– sbi
Apr 9 '15 at 4:46
I'm sorry, I think I had attempted a join of objects without << overloads, which I now realize is inappropriate for your code. I cannot cause your code to not compile with a vector of strings. On a side note, VS 2013 Community is both free and feature-ful, unlike "Express" versions.
– Grault
Apr 11 '15 at 2:52
@Jes: This should work with any type that can be streamed (i.e., hasoperator<<
overloaded). Of course, a type withoutoperator<<
might cause very confusing error messages.
– sbi
Apr 11 '15 at 7:41
Unfortunately, this doesn't compile:join(v.begin(), v.end(), ",")
. Template argument deduction doesn't produce the right result if thesep
argument is a string literal. My attempt at a solution for this issue. Also providing a more modern range-based overload.
– zett42
Sep 26 '18 at 19:58
add a comment |
Lots of template/ideas. Mine's not as generic or efficient, but I just had the same problem and wanted to throw this into the mix as something short and sweet. It wins on shortest number of lines... :)
std::stringstream joinedValues;
for (auto value: array)
{
joinedValues << value << ",";
}
//Strip off the trailing comma
std::string result = joinedValues.str().substr(0,joinedValues.str().size()-1);
1
Thanks for the simplistic code. Might want to change it to "auto &" though to avoid the extra copies and get some easy performance gains.
– Millie Smith
Sep 8 '17 at 4:31
Instead of usingsubstr(...)
, usepop_back()
to remove last character, becomes much more clear and clean then.
– ifyalciner
Dec 7 '17 at 23:31
add a comment |
If you want to do std::cout << join(myVector, ",") << std::endl;
, you can do something like:
template <typename C, typename T> class MyJoiner
{
C &c;
T &s;
MyJoiner(C &&container, T&& sep) : c(std::forward<C>(container)), s(std::forward<T>(sep)) {}
public:
template<typename C, typename T> friend std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj);
template<typename C, typename T> friend MyJoiner<C, T> join(C &&container, T&& sep);
};
template<typename C, typename T> std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj)
{
auto i = mj.c.begin();
if (i != mj.c.end())
{
o << *i++;
while (i != mj.c.end())
{
o << mj.s << *i++;
}
}
return o;
}
template<typename C, typename T> MyJoiner<C, T> join(C &&container, T&& sep)
{
return MyJoiner<C, T>(std::forward<C>(container), std::forward<T>(sep));
}
Note, this solution does the join directly into the output stream rather than creating a secondary buffer and will work with any types that have an operator<< onto an ostream.
This also works where boost::algorithm::join()
fails, when you have a vector<char*>
instead of a vector<string>
.
add a comment |
I like 1800's answer. However I would move the first iteration out of the loop as as the result of the if statement only changes once after the first iteration
template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
T result;
A it = begin;
if (it != end)
{
result.append(*it);
++it;
}
for( ;
it!=end;
++it)
{
result.append(t);
result.append(*it);
}
return result;
}
This can of course be reduced down to fewer statements if you like:
template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
T result;
A it = begin;
if (it != end)
result.append(*it++);
for( ; it!=end; ++it)
result.append(t).append(*it);
return result;
}
You shouldn't use post-increment for unknown iterator types. That might be expensive. (Of course, when dealing with strings, that might not make that much of a difference. But once you learn the habit...)
– sbi
Sep 16 '09 at 19:29
Post increment is fine as long as you use the tempory value that is returned. eg "result.append(*it); ++it;" is almost always as expensive as "result.append(*it++);" the second has one extra copy of the iterator.
– iain
Sep 17 '09 at 13:12
Oops I just spotted the post increment in the for loop. copy and paste error. I have fixed the post.
– iain
Sep 17 '09 at 13:20
1
@Ian: When I taught C++, I hammered into my students to use++i
except where they really neededi++
because that was the only way they wouldn't forget this when it made a difference. (It was the same with me, BTW.) They had learned Java before, where all kinds of C-isms are en vogue and it took them a few months (1 lecture +lab work per week), but in the end most of them learned the habit to use pre-increment.
– sbi
Sep 17 '09 at 14:50
1
@sbi: agreed I always default to preincrement too, the rogue postincrement came from copying someone elses for loop and changing it. In my first reply I thought you were worried about "result.append(*it++)" and not the for loop. I was a little embarrassed to see the post increment in the loop. Some people seem to follow the advice of not using post increment too far and never use it or change it even when it is appropriate. However I know now you don't fall in to this category.
– iain
Sep 18 '09 at 9:50
add a comment |
There are some interesting attempts at providing an elegant solution to the problem. I had an idea to use templated streams to effectively answer the OP's original dilemma. Though this is an old post, I'm hoping future users who stumble upon this will find my solution beneficial.
First, some answers (including the accepted answer) do not promote re-usability. Since C++ doesn't provide an elegant way to join strings in the standard library (that I have seen), it becomes important to create one that is flexible and reusable. Here's my shot at it:
// Replace with your namespace //
namespace my {
// Templated join which can be used on any combination of streams, iterators and base types //
template <typename TStream, typename TIter, typename TSeperator>
TStream& join(TStream& stream, TIter begin, TIter end, TSeperator seperator) {
// A flag which, when true, has next iteration prepend our seperator to the stream //
bool sep = false;
// Begin iterating through our list //
for (TIter i = begin; i != end; ++i) {
// If we need to prepend a seperator, do it //
if (sep) stream << seperator;
// Stream the next value held by our iterator //
stream << *i;
// Flag that next loops needs a seperator //
sep = true;
}
// As a convenience, we return a reference to the passed stream //
return stream;
}
}
Now to use this, you could simply do something like the following:
// Load some data //
std::vector<int> params;
params.push_back(1);
params.push_back(2);
params.push_back(3);
params.push_back(4);
// Store and print our results to standard out //
std::stringstream param_stream;
std::cout << my::join(param_stream, params.begin(), params.end(), ",").str() << std::endl;
// A quick and dirty way to print directly to standard out //
my::join(std::cout, params.begin(), params.end(), ",") << std::endl;
Note how the use of streams makes this solution incredibly flexible as we can store our result in a stringstream to reclaim it later, or we can write directly to the standard out, a file, or even to a network connection implemented as a stream. The type being printed must simply be iteratable and compatible with the source stream. STL provides various streams which are compatible with a large range of types. So you could really go to town with this. Off the top of my head, your vector can be of int, float, double, string, unsigned int, SomeObject*, and more.
add a comment |
string s;
for (auto i : v)
s += (s.empty() ? "" : ",") + to_string(i);
5
Welcome to Stack Overflow! While this code could solve the problem, it is best to add elaboration and explain how it works for people who might not understand this piece of code.
– paper1111
Jan 23 at 10:18
The current top answer is not much more elaborate, and this is the smallest/cleanest working answer. Not as efficient asstd::stringstream
for large arrays becausestringstream
will be able to allocate memory optimistically, leading to O(n.log(n)) performance instead of O(n²) for an array of sizen
for this answer. Alsostringstream
might not build temporary strings forto_string(i)
.
– aberaud
Feb 17 at 20:10
add a comment |
I've created an helper header file to add an extended join support.
Just add the code below to your general header file and include it when needed.
Usage Examples:
/* An example for a mapping function. */
ostream&
map_numbers(ostream& os, const void* payload, generic_primitive data)
{
static string names = {"Zero", "One", "Two", "Three", "Four"};
os << names[data.as_int];
const string* post = reinterpret_cast<const string*>(payload);
if (post) {
os << " " << *post;
}
return os;
}
int main() {
int arr = {0,1,2,3,4};
vector<int> vec(arr, arr + 5);
cout << vec << endl; /* Outputs: '0 1 2 3 4' */
cout << join(vec.begin(), vec.end()) << endl; /* Outputs: '0 1 2 3 4' */
cout << join(vec.begin(), vec.begin() + 2) << endl; /* Outputs: '0 1 2' */
cout << join(vec.begin(), vec.end(), ", ") << endl; /* Outputs: '0, 1, 2, 3, 4' */
cout << join(vec.begin(), vec.end(), ", ", map_numbers) << endl; /* Outputs: 'Zero, One, Two, Three, Four' */
string post = "Mississippi";
cout << join(vec.begin() + 1, vec.end(), ", ", map_numbers, &post) << endl; /* Outputs: 'One Mississippi, Two mississippi, Three mississippi, Four mississippi' */
return 0;
}
The code behind the scene:
#include <iostream>
#include <vector>
#include <list>
#include <set>
#include <unordered_set>
using namespace std;
#define GENERIC_PRIMITIVE_CLASS_BUILDER(T) generic_primitive(const T& v) { value.as_##T = v; }
#define GENERIC_PRIMITIVE_TYPE_BUILDER(T) T as_##T;
typedef void* ptr;
/** A union that could contain a primitive or void*,
* used for generic function pointers.
* TODO: add more primitive types as needed.
*/
struct generic_primitive {
GENERIC_PRIMITIVE_CLASS_BUILDER(int);
GENERIC_PRIMITIVE_CLASS_BUILDER(ptr);
union {
GENERIC_PRIMITIVE_TYPE_BUILDER(int);
GENERIC_PRIMITIVE_TYPE_BUILDER(ptr);
};
};
typedef ostream& (*mapping_funct_t)(ostream&, const void*, generic_primitive);
template<typename T>
class Join {
public:
Join(const T& begin, const T& end,
const string& separator = " ",
mapping_funct_t mapping = 0,
const void* payload = 0):
m_begin(begin),
m_end(end),
m_separator(separator),
m_mapping(mapping),
m_payload(payload) {}
ostream&
apply(ostream& os) const
{
T begin = m_begin;
T end = m_end;
if (begin != end)
if (m_mapping) {
m_mapping(os, m_payload, *begin++);
} else {
os << *begin++;
}
while (begin != end) {
os << m_separator;
if (m_mapping) {
m_mapping(os, m_payload, *begin++);
} else {
os << *begin++;
}
}
return os;
}
private:
const T& m_begin;
const T& m_end;
const string m_separator;
const mapping_funct_t m_mapping;
const void* m_payload;
};
template <typename T>
Join<T>
join(const T& begin, const T& end,
const string& separator = " ",
ostream& (*mapping)(ostream&, const void*, generic_primitive) = 0,
const void* payload = 0)
{
return Join<T>(begin, end, separator, mapping, payload);
}
template<typename T>
ostream&
operator<<(ostream& os, const vector<T>& vec) {
return join(vec.begin(), vec.end()).apply(os);
}
template<typename T>
ostream&
operator<<(ostream& os, const list<T>& lst) {
return join(lst.begin(), lst.end()).apply(os);
}
template<typename T>
ostream&
operator<<(ostream& os, const set<T>& s) {
return join(s.begin(), s.end()).apply(os);
}
template<typename T>
ostream&
operator<<(ostream& os, const Join<T>& vec) {
return vec.apply(os);
}
add a comment |
Here's a generic C++11 solution that will let you do
int main() {
vector<int> v {1,2,3};
cout << join(v, ", ") << endl;
string s = join(v, '+').str();
}
The code is:
template<typename Iterable, typename Sep>
class Joiner {
const Iterable& i_;
const Sep& s_;
public:
Joiner(const Iterable& i, const Sep& s) : i_(i), s_(s) {}
std::string str() const {std::stringstream ss; ss << *this; return ss.str();}
template<typename I, typename S> friend std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& j);
};
template<typename I, typename S>
std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& j) {
auto elem = j.i_.begin();
if (elem != j.i_.end()) {
os << *elem;
++elem;
while (elem != j.i_.end()) {
os << j.s_ << *elem;
++elem;
}
}
return os;
}
template<typename I, typename S>
inline Joiner<I,S> join(const I& i, const S& s) {return Joiner<I,S>(i, s);}
add a comment |
The following is a simple and practical way to convert elements in a vector
to a string
:
std::string join(const std::vector<int>& numbers, const std::string& delimiter = ",") {
std::ostringstream result;
for (const auto number : numbers) {
if (result.tellp() > 0) { // not first round
result << delimiter;
}
result << number;
}
return result.str();
}
You need to #include <sstream>
for ostringstream
.
add a comment |
as @capone did ,
std::string join(const std::vector<std::string> &str_list ,
const std::string &delim=" ")
{
if(str_list.size() == 0) return "" ;
return std::accumulate( str_list.cbegin() + 1,
str_list.cend(),
str_list.at(0) ,
[&delim](const std::string &a , const std::string &b)
{
return a + delim + b ;
} ) ;
}
template <typename ST , typename TT>
std::vector<TT> map(TT (*op)(ST) , const vector<ST> &ori_vec)
{
vector<TT> rst ;
std::transform(ori_vec.cbegin() ,
ori_vec.cend() , back_inserter(rst) ,
[&op](const ST& val){ return op(val) ;} ) ;
return rst ;
}
Then we can call like following :
int main(int argc , char *argv)
{
vector<int> int_vec = {1,2,3,4} ;
vector<string> str_vec = map<int,string>(to_string, int_vec) ;
cout << join(str_vec) << endl ;
return 0 ;
}
just like python :
>>> " ".join( map(str, [1,2,3,4]) )
add a comment |
I use something like this
namespace std
{
// for strings join
string to_string( string value )
{
return value;
}
} // namespace std
namespace // anonymous
{
template< typename T >
std::string join( const std::vector<T>& values, char delimiter )
{
std::string result;
for( typename std::vector<T>::size_type idx = 0; idx < values.size(); ++idx )
{
if( idx != 0 )
result += delimiter;
result += std::to_string( values[idx] );
}
return result;
}
} // namespace anonymous
add a comment |
I started out with @sbi's answer but most of the time ended up piping the resulting string to a stream so created the below solution that can be piped to a stream without the overhead of creating the full string in memory.
It is used as follows:
#include "string_join.h"
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3, 4 };
// String version
std::string str = join(v, std::string(", "));
std::cout << str << std::endl;
// Directly piped to stream version
std::cout << join(v, std::string(", ")) << std::endl;
}
Where string_join.h is:
#pragma once
#include <iterator>
#include <sstream>
template<typename Str, typename It>
class joined_strings
{
private:
const It begin, end;
Str sep;
public:
typedef typename Str::value_type char_type;
typedef typename Str::traits_type traits_type;
typedef typename Str::allocator_type allocator_type;
private:
typedef std::basic_ostringstream<char_type, traits_type, allocator_type>
ostringstream_type;
public:
joined_strings(It begin, const It end, const Str &sep)
: begin(begin), end(end), sep(sep)
{
}
operator Str() const
{
ostringstream_type result;
result << *this;
return result.str();
}
template<typename ostream_type>
friend ostream_type& operator<<(
ostream_type &ostr, const joined_strings<Str, It> &joined)
{
It it = joined.begin;
if(it!=joined.end)
ostr << *it;
for(++it; it!=joined.end; ++it)
ostr << joined.sep << *it;
return ostr;
}
};
template<typename Str, typename It>
inline joined_strings<Str, It> join(It begin, const It end, const Str &sep)
{
return joined_strings<Str, It>(begin, end, sep);
}
template<typename Str, typename Container>
inline joined_strings<Str, typename Container::const_iterator> join(
Container container, const Str &sep)
{
return join(container.cbegin(), container.cend(), sep);
}
add a comment |
I have wrote the following code. It is based in C# string.join. It works with std::string and std::wstring and many type of vectors. (examples in comments)
Call it like this:
std::vector<int> vVectorOfIds = {1, 2, 3, 4, 5};
std::wstring wstrStringForSQLIn = Join(vVectorOfIds, L',');
Code:
// Generic Join template (mimics string.Join() from C#)
// Written by RandomGuy (stackoverflow) 09-01-2017
// Based on Brian R. Bondy anwser here:
// http://stackoverflow.com/questions/1430757/c-vector-to-string
// Works with char, wchar_t, std::string and std::wstring delimiters
// Also works with a different types of vectors like ints, floats, longs
template<typename T, typename D>
auto Join(const std::vector<T> &vToMerge, const D &delimiter)
{
// We use std::conditional to get the correct type for the stringstream (char or wchar_t)
// stringstream = basic_stringstream<char>, wstringstream = basic_stringstream<wchar_t>
using strType =
std::conditional<
std::is_same<D, std::string>::value,
char,
std::conditional<
std::is_same<D, char>::value,
char,
wchar_t
>::type
>::type;
std::basic_stringstream<strType> ss;
for (size_t i = 0; i < vToMerge.size(); ++i)
{
if (i != 0)
ss << delimiter;
ss << vToMerge[i];
}
return ss.str();
}
add a comment |
Expanding on the attempt of @sbi at a generic solution that is not restricted to std::vector<int>
or a specific return string type. The code presented below can be used like this:
std::vector<int> vec{ 1, 2, 3 };
// Call modern range-based overload.
auto str = join( vec, "," );
auto wideStr = join( vec, L"," );
// Call old-school iterator-based overload.
auto str = join( vec.begin(), vec.end(), "," );
auto wideStr = join( vec.begin(), vec.end(), L"," );
In the original code, template argument deduction does not work to produce the right return string type if the separator is a string literal (as in the samples above). In this case, the typedefs like Str::value_type
in the function body are incorrect. The code assumes that Str
is always a type like std::basic_string
, so it obviously fails for string literals.
To fix this, the following code tries to deduce only the character type from the separator argument and uses that to produce a default return string type. This is achieved using boost::range_value
, which extracts the element type from the given range type.
#include <string>
#include <sstream>
#include <boost/range.hpp>
template< class Sep, class Str = std::basic_string< typename boost::range_value< Sep >::type >, class InputIt >
Str join( InputIt first, const InputIt last, const Sep& sep )
{
using char_type = typename Str::value_type;
using traits_type = typename Str::traits_type;
using allocator_type = typename Str::allocator_type;
using ostringstream_type = std::basic_ostringstream< char_type, traits_type, allocator_type >;
ostringstream_type result;
if( first != last )
{
result << *first++;
}
while( first != last )
{
result << sep << *first++;
}
return result.str();
}
Now we can easily provide a range-based overload that simply forwards to the iterator-based overload:
template <class Sep, class Str = std::basic_string< typename boost::range_value<Sep>::type >, class InputRange>
Str join( const InputRange &input, const Sep &sep )
{
// Include the standard begin() and end() in the overload set for ADL. This makes the
// function work for standard types (including arrays), aswell as any custom types
// that have begin() and end() member functions or overloads of the standalone functions.
using std::begin; using std::end;
// Call iterator-based overload.
return join( begin(input), end(input), sep );
}
Live Demo at Coliru
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f1430757%2fconvert-a-vectorint-to-a-string%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
19 Answers
19
active
oldest
votes
19 Answers
19
active
oldest
votes
active
oldest
votes
active
oldest
votes
Definitely not as elegant as Python, but nothing quite is as elegant as Python in C++.
You could use a stringstream
...
std::stringstream ss;
for(size_t i = 0; i < v.size(); ++i)
{
if(i != 0)
ss << ",";
ss << v[i];
}
std::string s = ss.str();
You could also make use of std::for_each
instead.
I think you mean array.size() not v.size(), no?
– Mark Elliot
Sep 16 '09 at 3:35
1
ya whatever the vector is called.
– Brian R. Bondy
Sep 16 '09 at 3:37
2
That should bestd::string s = ss.str()
. If you want aconst char*
, uses.c_str()
. (Note that, while syntactically correct,ss.str().c_str()
will give you aconst char*
that points to a temporary which will will cease to exist at the end of the full expression. That hurts.)
– sbi
Sep 16 '09 at 8:56
1
why not just use string.append?
– Baiyan Huang
Jun 27 '14 at 9:56
11
answer is incomplete without#include <sstream>
– renadeen
Mar 20 '16 at 20:02
|
show 2 more comments
Definitely not as elegant as Python, but nothing quite is as elegant as Python in C++.
You could use a stringstream
...
std::stringstream ss;
for(size_t i = 0; i < v.size(); ++i)
{
if(i != 0)
ss << ",";
ss << v[i];
}
std::string s = ss.str();
You could also make use of std::for_each
instead.
I think you mean array.size() not v.size(), no?
– Mark Elliot
Sep 16 '09 at 3:35
1
ya whatever the vector is called.
– Brian R. Bondy
Sep 16 '09 at 3:37
2
That should bestd::string s = ss.str()
. If you want aconst char*
, uses.c_str()
. (Note that, while syntactically correct,ss.str().c_str()
will give you aconst char*
that points to a temporary which will will cease to exist at the end of the full expression. That hurts.)
– sbi
Sep 16 '09 at 8:56
1
why not just use string.append?
– Baiyan Huang
Jun 27 '14 at 9:56
11
answer is incomplete without#include <sstream>
– renadeen
Mar 20 '16 at 20:02
|
show 2 more comments
Definitely not as elegant as Python, but nothing quite is as elegant as Python in C++.
You could use a stringstream
...
std::stringstream ss;
for(size_t i = 0; i < v.size(); ++i)
{
if(i != 0)
ss << ",";
ss << v[i];
}
std::string s = ss.str();
You could also make use of std::for_each
instead.
Definitely not as elegant as Python, but nothing quite is as elegant as Python in C++.
You could use a stringstream
...
std::stringstream ss;
for(size_t i = 0; i < v.size(); ++i)
{
if(i != 0)
ss << ",";
ss << v[i];
}
std::string s = ss.str();
You could also make use of std::for_each
instead.
edited Oct 6 '15 at 15:02
cdmh
2,53411839
2,53411839
answered Sep 16 '09 at 3:19
Brian R. BondyBrian R. Bondy
257k99545595
257k99545595
I think you mean array.size() not v.size(), no?
– Mark Elliot
Sep 16 '09 at 3:35
1
ya whatever the vector is called.
– Brian R. Bondy
Sep 16 '09 at 3:37
2
That should bestd::string s = ss.str()
. If you want aconst char*
, uses.c_str()
. (Note that, while syntactically correct,ss.str().c_str()
will give you aconst char*
that points to a temporary which will will cease to exist at the end of the full expression. That hurts.)
– sbi
Sep 16 '09 at 8:56
1
why not just use string.append?
– Baiyan Huang
Jun 27 '14 at 9:56
11
answer is incomplete without#include <sstream>
– renadeen
Mar 20 '16 at 20:02
|
show 2 more comments
I think you mean array.size() not v.size(), no?
– Mark Elliot
Sep 16 '09 at 3:35
1
ya whatever the vector is called.
– Brian R. Bondy
Sep 16 '09 at 3:37
2
That should bestd::string s = ss.str()
. If you want aconst char*
, uses.c_str()
. (Note that, while syntactically correct,ss.str().c_str()
will give you aconst char*
that points to a temporary which will will cease to exist at the end of the full expression. That hurts.)
– sbi
Sep 16 '09 at 8:56
1
why not just use string.append?
– Baiyan Huang
Jun 27 '14 at 9:56
11
answer is incomplete without#include <sstream>
– renadeen
Mar 20 '16 at 20:02
I think you mean array.size() not v.size(), no?
– Mark Elliot
Sep 16 '09 at 3:35
I think you mean array.size() not v.size(), no?
– Mark Elliot
Sep 16 '09 at 3:35
1
1
ya whatever the vector is called.
– Brian R. Bondy
Sep 16 '09 at 3:37
ya whatever the vector is called.
– Brian R. Bondy
Sep 16 '09 at 3:37
2
2
That should be
std::string s = ss.str()
. If you want a const char*
, use s.c_str()
. (Note that, while syntactically correct, ss.str().c_str()
will give you a const char*
that points to a temporary which will will cease to exist at the end of the full expression. That hurts.)– sbi
Sep 16 '09 at 8:56
That should be
std::string s = ss.str()
. If you want a const char*
, use s.c_str()
. (Note that, while syntactically correct, ss.str().c_str()
will give you a const char*
that points to a temporary which will will cease to exist at the end of the full expression. That hurts.)– sbi
Sep 16 '09 at 8:56
1
1
why not just use string.append?
– Baiyan Huang
Jun 27 '14 at 9:56
why not just use string.append?
– Baiyan Huang
Jun 27 '14 at 9:56
11
11
answer is incomplete without
#include <sstream>
– renadeen
Mar 20 '16 at 20:02
answer is incomplete without
#include <sstream>
– renadeen
Mar 20 '16 at 20:02
|
show 2 more comments
Using std::copy and std::ostream_iterator we can get something as elegant as python.
#include <iostream>
#include <sstream>
#include <algorithm>
#include <iterator>
int main()
{
int array = {1,2,3,4};
std::copy(array, array+4, std::ostream_iterator<int>(std::cout,","));
}
See this question for a little class I wrote. This will not print the trailing comma. Also if we assume that C++14 will continue to give us range based equivalents of algorithms like this:
namespace std {
// I am assuming something like this in the C++14 standard
// I have no idea if this is correct but it should be trivial to write if it does not appear.
template<typename C, typename I>
void copy(C const& container, I outputIter) {copy(begin(container), end(container), outputIter);}
}
using POI = PrefexOutputIterator;
int main()
{
int array = {1,2,3,4};
std::copy(array, POI(std::cout, ","));
// ",".join(map(str,array)) // closer
}
11
I think this is not quite equivalent to Python's join - it will insert an extra "," at the end.
– 1800 INFORMATION
Sep 16 '09 at 4:13
2
Not equivalent but just as elegant (in fact I think more so but that is just an opinion).
– Martin York
Sep 16 '09 at 4:36
19
Obviously elegance is subjective. So if you and two other people prefer longer, more repetitive code that doesn't work, then it's more elegant ;-p
– Steve Jessop
Sep 16 '09 at 10:19
Why doesn't it work?
– jalf
Sep 16 '09 at 12:31
1
You can ignore the final comma by using the string::substr member function. Assign the the substring from 0 to n-1 to your result variable.
– Dan
Sep 16 '09 at 15:59
|
show 2 more comments
Using std::copy and std::ostream_iterator we can get something as elegant as python.
#include <iostream>
#include <sstream>
#include <algorithm>
#include <iterator>
int main()
{
int array = {1,2,3,4};
std::copy(array, array+4, std::ostream_iterator<int>(std::cout,","));
}
See this question for a little class I wrote. This will not print the trailing comma. Also if we assume that C++14 will continue to give us range based equivalents of algorithms like this:
namespace std {
// I am assuming something like this in the C++14 standard
// I have no idea if this is correct but it should be trivial to write if it does not appear.
template<typename C, typename I>
void copy(C const& container, I outputIter) {copy(begin(container), end(container), outputIter);}
}
using POI = PrefexOutputIterator;
int main()
{
int array = {1,2,3,4};
std::copy(array, POI(std::cout, ","));
// ",".join(map(str,array)) // closer
}
11
I think this is not quite equivalent to Python's join - it will insert an extra "," at the end.
– 1800 INFORMATION
Sep 16 '09 at 4:13
2
Not equivalent but just as elegant (in fact I think more so but that is just an opinion).
– Martin York
Sep 16 '09 at 4:36
19
Obviously elegance is subjective. So if you and two other people prefer longer, more repetitive code that doesn't work, then it's more elegant ;-p
– Steve Jessop
Sep 16 '09 at 10:19
Why doesn't it work?
– jalf
Sep 16 '09 at 12:31
1
You can ignore the final comma by using the string::substr member function. Assign the the substring from 0 to n-1 to your result variable.
– Dan
Sep 16 '09 at 15:59
|
show 2 more comments
Using std::copy and std::ostream_iterator we can get something as elegant as python.
#include <iostream>
#include <sstream>
#include <algorithm>
#include <iterator>
int main()
{
int array = {1,2,3,4};
std::copy(array, array+4, std::ostream_iterator<int>(std::cout,","));
}
See this question for a little class I wrote. This will not print the trailing comma. Also if we assume that C++14 will continue to give us range based equivalents of algorithms like this:
namespace std {
// I am assuming something like this in the C++14 standard
// I have no idea if this is correct but it should be trivial to write if it does not appear.
template<typename C, typename I>
void copy(C const& container, I outputIter) {copy(begin(container), end(container), outputIter);}
}
using POI = PrefexOutputIterator;
int main()
{
int array = {1,2,3,4};
std::copy(array, POI(std::cout, ","));
// ",".join(map(str,array)) // closer
}
Using std::copy and std::ostream_iterator we can get something as elegant as python.
#include <iostream>
#include <sstream>
#include <algorithm>
#include <iterator>
int main()
{
int array = {1,2,3,4};
std::copy(array, array+4, std::ostream_iterator<int>(std::cout,","));
}
See this question for a little class I wrote. This will not print the trailing comma. Also if we assume that C++14 will continue to give us range based equivalents of algorithms like this:
namespace std {
// I am assuming something like this in the C++14 standard
// I have no idea if this is correct but it should be trivial to write if it does not appear.
template<typename C, typename I>
void copy(C const& container, I outputIter) {copy(begin(container), end(container), outputIter);}
}
using POI = PrefexOutputIterator;
int main()
{
int array = {1,2,3,4};
std::copy(array, POI(std::cout, ","));
// ",".join(map(str,array)) // closer
}
edited Apr 13 '17 at 12:40
Community♦
11
11
answered Sep 16 '09 at 4:09
Martin YorkMartin York
201k67270487
201k67270487
11
I think this is not quite equivalent to Python's join - it will insert an extra "," at the end.
– 1800 INFORMATION
Sep 16 '09 at 4:13
2
Not equivalent but just as elegant (in fact I think more so but that is just an opinion).
– Martin York
Sep 16 '09 at 4:36
19
Obviously elegance is subjective. So if you and two other people prefer longer, more repetitive code that doesn't work, then it's more elegant ;-p
– Steve Jessop
Sep 16 '09 at 10:19
Why doesn't it work?
– jalf
Sep 16 '09 at 12:31
1
You can ignore the final comma by using the string::substr member function. Assign the the substring from 0 to n-1 to your result variable.
– Dan
Sep 16 '09 at 15:59
|
show 2 more comments
11
I think this is not quite equivalent to Python's join - it will insert an extra "," at the end.
– 1800 INFORMATION
Sep 16 '09 at 4:13
2
Not equivalent but just as elegant (in fact I think more so but that is just an opinion).
– Martin York
Sep 16 '09 at 4:36
19
Obviously elegance is subjective. So if you and two other people prefer longer, more repetitive code that doesn't work, then it's more elegant ;-p
– Steve Jessop
Sep 16 '09 at 10:19
Why doesn't it work?
– jalf
Sep 16 '09 at 12:31
1
You can ignore the final comma by using the string::substr member function. Assign the the substring from 0 to n-1 to your result variable.
– Dan
Sep 16 '09 at 15:59
11
11
I think this is not quite equivalent to Python's join - it will insert an extra "," at the end.
– 1800 INFORMATION
Sep 16 '09 at 4:13
I think this is not quite equivalent to Python's join - it will insert an extra "," at the end.
– 1800 INFORMATION
Sep 16 '09 at 4:13
2
2
Not equivalent but just as elegant (in fact I think more so but that is just an opinion).
– Martin York
Sep 16 '09 at 4:36
Not equivalent but just as elegant (in fact I think more so but that is just an opinion).
– Martin York
Sep 16 '09 at 4:36
19
19
Obviously elegance is subjective. So if you and two other people prefer longer, more repetitive code that doesn't work, then it's more elegant ;-p
– Steve Jessop
Sep 16 '09 at 10:19
Obviously elegance is subjective. So if you and two other people prefer longer, more repetitive code that doesn't work, then it's more elegant ;-p
– Steve Jessop
Sep 16 '09 at 10:19
Why doesn't it work?
– jalf
Sep 16 '09 at 12:31
Why doesn't it work?
– jalf
Sep 16 '09 at 12:31
1
1
You can ignore the final comma by using the string::substr member function. Assign the the substring from 0 to n-1 to your result variable.
– Dan
Sep 16 '09 at 15:59
You can ignore the final comma by using the string::substr member function. Assign the the substring from 0 to n-1 to your result variable.
– Dan
Sep 16 '09 at 15:59
|
show 2 more comments
Another alternative is the use of std::copy
and the ostream_iterator
class:
#include <iterator> // ostream_iterator
#include <sstream> // ostringstream
#include <algorithm> // copy
std::ostringstream stream;
std::copy(array.begin(), array.end(), std::ostream_iterator<>(stream));
std::string s=stream.str();
s.erase(s.length()-1);
Also not as nice as Python.
For this purpose, I created a join
function:
template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
T result;
for (A it=begin;
it!=end;
it++)
{
if (!result.empty())
result.append(t);
result.append(*it);
}
return result;
}
Then used it like this:
std::string s=join(array.begin(), array.end(), std::string(","));
You might ask why I passed in the iterators. Well, actually I wanted to reverse the array, so I used it like this:
std::string s=join(array.rbegin(), array.rend(), std::string(","));
Ideally, I would like to template out to the point where it can infer the char type, and use string-streams, but I couldn't figure that out yet.
Since it would be too much for a comment, I have posted an answer (stackoverflow.com/questions/1430757/1432040#1432040) which attempts to solve the riddle given in your last sentence.
– sbi
Sep 16 '09 at 9:51
Yourjoin
function can be used with vectors as well? May you please give example, I'm new to C++.
– Noitidart
Jan 15 '17 at 1:09
Can you change the iterator to a preincrement in the answer?
– Millie Smith
Sep 8 '17 at 3:12
add a comment |
Another alternative is the use of std::copy
and the ostream_iterator
class:
#include <iterator> // ostream_iterator
#include <sstream> // ostringstream
#include <algorithm> // copy
std::ostringstream stream;
std::copy(array.begin(), array.end(), std::ostream_iterator<>(stream));
std::string s=stream.str();
s.erase(s.length()-1);
Also not as nice as Python.
For this purpose, I created a join
function:
template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
T result;
for (A it=begin;
it!=end;
it++)
{
if (!result.empty())
result.append(t);
result.append(*it);
}
return result;
}
Then used it like this:
std::string s=join(array.begin(), array.end(), std::string(","));
You might ask why I passed in the iterators. Well, actually I wanted to reverse the array, so I used it like this:
std::string s=join(array.rbegin(), array.rend(), std::string(","));
Ideally, I would like to template out to the point where it can infer the char type, and use string-streams, but I couldn't figure that out yet.
Since it would be too much for a comment, I have posted an answer (stackoverflow.com/questions/1430757/1432040#1432040) which attempts to solve the riddle given in your last sentence.
– sbi
Sep 16 '09 at 9:51
Yourjoin
function can be used with vectors as well? May you please give example, I'm new to C++.
– Noitidart
Jan 15 '17 at 1:09
Can you change the iterator to a preincrement in the answer?
– Millie Smith
Sep 8 '17 at 3:12
add a comment |
Another alternative is the use of std::copy
and the ostream_iterator
class:
#include <iterator> // ostream_iterator
#include <sstream> // ostringstream
#include <algorithm> // copy
std::ostringstream stream;
std::copy(array.begin(), array.end(), std::ostream_iterator<>(stream));
std::string s=stream.str();
s.erase(s.length()-1);
Also not as nice as Python.
For this purpose, I created a join
function:
template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
T result;
for (A it=begin;
it!=end;
it++)
{
if (!result.empty())
result.append(t);
result.append(*it);
}
return result;
}
Then used it like this:
std::string s=join(array.begin(), array.end(), std::string(","));
You might ask why I passed in the iterators. Well, actually I wanted to reverse the array, so I used it like this:
std::string s=join(array.rbegin(), array.rend(), std::string(","));
Ideally, I would like to template out to the point where it can infer the char type, and use string-streams, but I couldn't figure that out yet.
Another alternative is the use of std::copy
and the ostream_iterator
class:
#include <iterator> // ostream_iterator
#include <sstream> // ostringstream
#include <algorithm> // copy
std::ostringstream stream;
std::copy(array.begin(), array.end(), std::ostream_iterator<>(stream));
std::string s=stream.str();
s.erase(s.length()-1);
Also not as nice as Python.
For this purpose, I created a join
function:
template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
T result;
for (A it=begin;
it!=end;
it++)
{
if (!result.empty())
result.append(t);
result.append(*it);
}
return result;
}
Then used it like this:
std::string s=join(array.begin(), array.end(), std::string(","));
You might ask why I passed in the iterators. Well, actually I wanted to reverse the array, so I used it like this:
std::string s=join(array.rbegin(), array.rend(), std::string(","));
Ideally, I would like to template out to the point where it can infer the char type, and use string-streams, but I couldn't figure that out yet.
edited Apr 20 '15 at 10:18
user1556814
119111
119111
answered Sep 16 '09 at 4:09
1800 INFORMATION1800 INFORMATION
100k23138224
100k23138224
Since it would be too much for a comment, I have posted an answer (stackoverflow.com/questions/1430757/1432040#1432040) which attempts to solve the riddle given in your last sentence.
– sbi
Sep 16 '09 at 9:51
Yourjoin
function can be used with vectors as well? May you please give example, I'm new to C++.
– Noitidart
Jan 15 '17 at 1:09
Can you change the iterator to a preincrement in the answer?
– Millie Smith
Sep 8 '17 at 3:12
add a comment |
Since it would be too much for a comment, I have posted an answer (stackoverflow.com/questions/1430757/1432040#1432040) which attempts to solve the riddle given in your last sentence.
– sbi
Sep 16 '09 at 9:51
Yourjoin
function can be used with vectors as well? May you please give example, I'm new to C++.
– Noitidart
Jan 15 '17 at 1:09
Can you change the iterator to a preincrement in the answer?
– Millie Smith
Sep 8 '17 at 3:12
Since it would be too much for a comment, I have posted an answer (stackoverflow.com/questions/1430757/1432040#1432040) which attempts to solve the riddle given in your last sentence.
– sbi
Sep 16 '09 at 9:51
Since it would be too much for a comment, I have posted an answer (stackoverflow.com/questions/1430757/1432040#1432040) which attempts to solve the riddle given in your last sentence.
– sbi
Sep 16 '09 at 9:51
Your
join
function can be used with vectors as well? May you please give example, I'm new to C++.– Noitidart
Jan 15 '17 at 1:09
Your
join
function can be used with vectors as well? May you please give example, I'm new to C++.– Noitidart
Jan 15 '17 at 1:09
Can you change the iterator to a preincrement in the answer?
– Millie Smith
Sep 8 '17 at 3:12
Can you change the iterator to a preincrement in the answer?
– Millie Smith
Sep 8 '17 at 3:12
add a comment |
You can use std::accumulate. Consider the following example
if (v.empty()
return std::string();
std::string s = std::accumulate(v.begin()+1, v.end(), std::to_string(v[0]),
(const std::string& a, int b){
return a + ',' + std::to_string(b);
});
add a comment |
You can use std::accumulate. Consider the following example
if (v.empty()
return std::string();
std::string s = std::accumulate(v.begin()+1, v.end(), std::to_string(v[0]),
(const std::string& a, int b){
return a + ',' + std::to_string(b);
});
add a comment |
You can use std::accumulate. Consider the following example
if (v.empty()
return std::string();
std::string s = std::accumulate(v.begin()+1, v.end(), std::to_string(v[0]),
(const std::string& a, int b){
return a + ',' + std::to_string(b);
});
You can use std::accumulate. Consider the following example
if (v.empty()
return std::string();
std::string s = std::accumulate(v.begin()+1, v.end(), std::to_string(v[0]),
(const std::string& a, int b){
return a + ',' + std::to_string(b);
});
answered May 31 '15 at 11:06
caponecapone
398214
398214
add a comment |
add a comment |
With Boost and C++11 this could be achieved like this:
auto array = {1,2,3,4};
join(array | transformed(tostr), ",");
Well, almost. Here's the full example:
#include <array>
#include <iostream>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>
int main() {
using boost::algorithm::join;
using boost::adaptors::transformed;
auto tostr = static_cast<std::string(*)(int)>(std::to_string);
auto array = {1,2,3,4};
std::cout << join(array | transformed(tostr), ",") << std::endl;
return 0;
}
Credit to Praetorian.
You can handle any value type like this:
template<class Container>
std::string join(Container const & container, std::string delimiter) {
using boost::algorithm::join;
using boost::adaptors::transformed;
using value_type = typename Container::value_type;
auto tostr = static_cast<std::string(*)(value_type)>(std::to_string);
return join(container | transformed(tostr), delimiter);
};
add a comment |
With Boost and C++11 this could be achieved like this:
auto array = {1,2,3,4};
join(array | transformed(tostr), ",");
Well, almost. Here's the full example:
#include <array>
#include <iostream>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>
int main() {
using boost::algorithm::join;
using boost::adaptors::transformed;
auto tostr = static_cast<std::string(*)(int)>(std::to_string);
auto array = {1,2,3,4};
std::cout << join(array | transformed(tostr), ",") << std::endl;
return 0;
}
Credit to Praetorian.
You can handle any value type like this:
template<class Container>
std::string join(Container const & container, std::string delimiter) {
using boost::algorithm::join;
using boost::adaptors::transformed;
using value_type = typename Container::value_type;
auto tostr = static_cast<std::string(*)(value_type)>(std::to_string);
return join(container | transformed(tostr), delimiter);
};
add a comment |
With Boost and C++11 this could be achieved like this:
auto array = {1,2,3,4};
join(array | transformed(tostr), ",");
Well, almost. Here's the full example:
#include <array>
#include <iostream>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>
int main() {
using boost::algorithm::join;
using boost::adaptors::transformed;
auto tostr = static_cast<std::string(*)(int)>(std::to_string);
auto array = {1,2,3,4};
std::cout << join(array | transformed(tostr), ",") << std::endl;
return 0;
}
Credit to Praetorian.
You can handle any value type like this:
template<class Container>
std::string join(Container const & container, std::string delimiter) {
using boost::algorithm::join;
using boost::adaptors::transformed;
using value_type = typename Container::value_type;
auto tostr = static_cast<std::string(*)(value_type)>(std::to_string);
return join(container | transformed(tostr), delimiter);
};
With Boost and C++11 this could be achieved like this:
auto array = {1,2,3,4};
join(array | transformed(tostr), ",");
Well, almost. Here's the full example:
#include <array>
#include <iostream>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>
int main() {
using boost::algorithm::join;
using boost::adaptors::transformed;
auto tostr = static_cast<std::string(*)(int)>(std::to_string);
auto array = {1,2,3,4};
std::cout << join(array | transformed(tostr), ",") << std::endl;
return 0;
}
Credit to Praetorian.
You can handle any value type like this:
template<class Container>
std::string join(Container const & container, std::string delimiter) {
using boost::algorithm::join;
using boost::adaptors::transformed;
using value_type = typename Container::value_type;
auto tostr = static_cast<std::string(*)(value_type)>(std::to_string);
return join(container | transformed(tostr), delimiter);
};
edited May 23 '17 at 11:33
Community♦
11
11
answered Oct 6 '15 at 14:25
arekolekarekolek
4,6213250
4,6213250
add a comment |
add a comment |
This is just an attempt to solve the riddle given by 1800 INFORMATION's remark on his second solution lacking genericity, not an attempt to answer the question:
template <class Str, class It>
Str join(It begin, const It end, const Str &sep)
{
typedef typename Str::value_type char_type;
typedef typename Str::traits_type traits_type;
typedef typename Str::allocator_type allocator_type;
typedef std::basic_ostringstream<char_type,traits_type,allocator_type>
ostringstream_type;
ostringstream_type result;
if(begin!=end)
result << *begin++;
while(begin!=end) {
result << sep;
result << *begin++;
}
return result.str();
}
Works On My Machine(TM).
Visual Studio 2013 gets very confused by the typedefs. Not that you could have known that in 2009.
– Grault
Apr 9 '15 at 4:01
@Jes: I have now been staring at this for 5mins, but couldn't figure out what VS might trip over. Can you be more specific?
– sbi
Apr 9 '15 at 4:46
I'm sorry, I think I had attempted a join of objects without << overloads, which I now realize is inappropriate for your code. I cannot cause your code to not compile with a vector of strings. On a side note, VS 2013 Community is both free and feature-ful, unlike "Express" versions.
– Grault
Apr 11 '15 at 2:52
@Jes: This should work with any type that can be streamed (i.e., hasoperator<<
overloaded). Of course, a type withoutoperator<<
might cause very confusing error messages.
– sbi
Apr 11 '15 at 7:41
Unfortunately, this doesn't compile:join(v.begin(), v.end(), ",")
. Template argument deduction doesn't produce the right result if thesep
argument is a string literal. My attempt at a solution for this issue. Also providing a more modern range-based overload.
– zett42
Sep 26 '18 at 19:58
add a comment |
This is just an attempt to solve the riddle given by 1800 INFORMATION's remark on his second solution lacking genericity, not an attempt to answer the question:
template <class Str, class It>
Str join(It begin, const It end, const Str &sep)
{
typedef typename Str::value_type char_type;
typedef typename Str::traits_type traits_type;
typedef typename Str::allocator_type allocator_type;
typedef std::basic_ostringstream<char_type,traits_type,allocator_type>
ostringstream_type;
ostringstream_type result;
if(begin!=end)
result << *begin++;
while(begin!=end) {
result << sep;
result << *begin++;
}
return result.str();
}
Works On My Machine(TM).
Visual Studio 2013 gets very confused by the typedefs. Not that you could have known that in 2009.
– Grault
Apr 9 '15 at 4:01
@Jes: I have now been staring at this for 5mins, but couldn't figure out what VS might trip over. Can you be more specific?
– sbi
Apr 9 '15 at 4:46
I'm sorry, I think I had attempted a join of objects without << overloads, which I now realize is inappropriate for your code. I cannot cause your code to not compile with a vector of strings. On a side note, VS 2013 Community is both free and feature-ful, unlike "Express" versions.
– Grault
Apr 11 '15 at 2:52
@Jes: This should work with any type that can be streamed (i.e., hasoperator<<
overloaded). Of course, a type withoutoperator<<
might cause very confusing error messages.
– sbi
Apr 11 '15 at 7:41
Unfortunately, this doesn't compile:join(v.begin(), v.end(), ",")
. Template argument deduction doesn't produce the right result if thesep
argument is a string literal. My attempt at a solution for this issue. Also providing a more modern range-based overload.
– zett42
Sep 26 '18 at 19:58
add a comment |
This is just an attempt to solve the riddle given by 1800 INFORMATION's remark on his second solution lacking genericity, not an attempt to answer the question:
template <class Str, class It>
Str join(It begin, const It end, const Str &sep)
{
typedef typename Str::value_type char_type;
typedef typename Str::traits_type traits_type;
typedef typename Str::allocator_type allocator_type;
typedef std::basic_ostringstream<char_type,traits_type,allocator_type>
ostringstream_type;
ostringstream_type result;
if(begin!=end)
result << *begin++;
while(begin!=end) {
result << sep;
result << *begin++;
}
return result.str();
}
Works On My Machine(TM).
This is just an attempt to solve the riddle given by 1800 INFORMATION's remark on his second solution lacking genericity, not an attempt to answer the question:
template <class Str, class It>
Str join(It begin, const It end, const Str &sep)
{
typedef typename Str::value_type char_type;
typedef typename Str::traits_type traits_type;
typedef typename Str::allocator_type allocator_type;
typedef std::basic_ostringstream<char_type,traits_type,allocator_type>
ostringstream_type;
ostringstream_type result;
if(begin!=end)
result << *begin++;
while(begin!=end) {
result << sep;
result << *begin++;
}
return result.str();
}
Works On My Machine(TM).
edited May 23 '17 at 12:03
Community♦
11
11
answered Sep 16 '09 at 9:50
sbisbi
168k41218403
168k41218403
Visual Studio 2013 gets very confused by the typedefs. Not that you could have known that in 2009.
– Grault
Apr 9 '15 at 4:01
@Jes: I have now been staring at this for 5mins, but couldn't figure out what VS might trip over. Can you be more specific?
– sbi
Apr 9 '15 at 4:46
I'm sorry, I think I had attempted a join of objects without << overloads, which I now realize is inappropriate for your code. I cannot cause your code to not compile with a vector of strings. On a side note, VS 2013 Community is both free and feature-ful, unlike "Express" versions.
– Grault
Apr 11 '15 at 2:52
@Jes: This should work with any type that can be streamed (i.e., hasoperator<<
overloaded). Of course, a type withoutoperator<<
might cause very confusing error messages.
– sbi
Apr 11 '15 at 7:41
Unfortunately, this doesn't compile:join(v.begin(), v.end(), ",")
. Template argument deduction doesn't produce the right result if thesep
argument is a string literal. My attempt at a solution for this issue. Also providing a more modern range-based overload.
– zett42
Sep 26 '18 at 19:58
add a comment |
Visual Studio 2013 gets very confused by the typedefs. Not that you could have known that in 2009.
– Grault
Apr 9 '15 at 4:01
@Jes: I have now been staring at this for 5mins, but couldn't figure out what VS might trip over. Can you be more specific?
– sbi
Apr 9 '15 at 4:46
I'm sorry, I think I had attempted a join of objects without << overloads, which I now realize is inappropriate for your code. I cannot cause your code to not compile with a vector of strings. On a side note, VS 2013 Community is both free and feature-ful, unlike "Express" versions.
– Grault
Apr 11 '15 at 2:52
@Jes: This should work with any type that can be streamed (i.e., hasoperator<<
overloaded). Of course, a type withoutoperator<<
might cause very confusing error messages.
– sbi
Apr 11 '15 at 7:41
Unfortunately, this doesn't compile:join(v.begin(), v.end(), ",")
. Template argument deduction doesn't produce the right result if thesep
argument is a string literal. My attempt at a solution for this issue. Also providing a more modern range-based overload.
– zett42
Sep 26 '18 at 19:58
Visual Studio 2013 gets very confused by the typedefs. Not that you could have known that in 2009.
– Grault
Apr 9 '15 at 4:01
Visual Studio 2013 gets very confused by the typedefs. Not that you could have known that in 2009.
– Grault
Apr 9 '15 at 4:01
@Jes: I have now been staring at this for 5mins, but couldn't figure out what VS might trip over. Can you be more specific?
– sbi
Apr 9 '15 at 4:46
@Jes: I have now been staring at this for 5mins, but couldn't figure out what VS might trip over. Can you be more specific?
– sbi
Apr 9 '15 at 4:46
I'm sorry, I think I had attempted a join of objects without << overloads, which I now realize is inappropriate for your code. I cannot cause your code to not compile with a vector of strings. On a side note, VS 2013 Community is both free and feature-ful, unlike "Express" versions.
– Grault
Apr 11 '15 at 2:52
I'm sorry, I think I had attempted a join of objects without << overloads, which I now realize is inappropriate for your code. I cannot cause your code to not compile with a vector of strings. On a side note, VS 2013 Community is both free and feature-ful, unlike "Express" versions.
– Grault
Apr 11 '15 at 2:52
@Jes: This should work with any type that can be streamed (i.e., has
operator<<
overloaded). Of course, a type without operator<<
might cause very confusing error messages.– sbi
Apr 11 '15 at 7:41
@Jes: This should work with any type that can be streamed (i.e., has
operator<<
overloaded). Of course, a type without operator<<
might cause very confusing error messages.– sbi
Apr 11 '15 at 7:41
Unfortunately, this doesn't compile:
join(v.begin(), v.end(), ",")
. Template argument deduction doesn't produce the right result if the sep
argument is a string literal. My attempt at a solution for this issue. Also providing a more modern range-based overload.– zett42
Sep 26 '18 at 19:58
Unfortunately, this doesn't compile:
join(v.begin(), v.end(), ",")
. Template argument deduction doesn't produce the right result if the sep
argument is a string literal. My attempt at a solution for this issue. Also providing a more modern range-based overload.– zett42
Sep 26 '18 at 19:58
add a comment |
Lots of template/ideas. Mine's not as generic or efficient, but I just had the same problem and wanted to throw this into the mix as something short and sweet. It wins on shortest number of lines... :)
std::stringstream joinedValues;
for (auto value: array)
{
joinedValues << value << ",";
}
//Strip off the trailing comma
std::string result = joinedValues.str().substr(0,joinedValues.str().size()-1);
1
Thanks for the simplistic code. Might want to change it to "auto &" though to avoid the extra copies and get some easy performance gains.
– Millie Smith
Sep 8 '17 at 4:31
Instead of usingsubstr(...)
, usepop_back()
to remove last character, becomes much more clear and clean then.
– ifyalciner
Dec 7 '17 at 23:31
add a comment |
Lots of template/ideas. Mine's not as generic or efficient, but I just had the same problem and wanted to throw this into the mix as something short and sweet. It wins on shortest number of lines... :)
std::stringstream joinedValues;
for (auto value: array)
{
joinedValues << value << ",";
}
//Strip off the trailing comma
std::string result = joinedValues.str().substr(0,joinedValues.str().size()-1);
1
Thanks for the simplistic code. Might want to change it to "auto &" though to avoid the extra copies and get some easy performance gains.
– Millie Smith
Sep 8 '17 at 4:31
Instead of usingsubstr(...)
, usepop_back()
to remove last character, becomes much more clear and clean then.
– ifyalciner
Dec 7 '17 at 23:31
add a comment |
Lots of template/ideas. Mine's not as generic or efficient, but I just had the same problem and wanted to throw this into the mix as something short and sweet. It wins on shortest number of lines... :)
std::stringstream joinedValues;
for (auto value: array)
{
joinedValues << value << ",";
}
//Strip off the trailing comma
std::string result = joinedValues.str().substr(0,joinedValues.str().size()-1);
Lots of template/ideas. Mine's not as generic or efficient, but I just had the same problem and wanted to throw this into the mix as something short and sweet. It wins on shortest number of lines... :)
std::stringstream joinedValues;
for (auto value: array)
{
joinedValues << value << ",";
}
//Strip off the trailing comma
std::string result = joinedValues.str().substr(0,joinedValues.str().size()-1);
answered May 1 '14 at 18:48
Joe SchneiderJoe Schneider
5,32473457
5,32473457
1
Thanks for the simplistic code. Might want to change it to "auto &" though to avoid the extra copies and get some easy performance gains.
– Millie Smith
Sep 8 '17 at 4:31
Instead of usingsubstr(...)
, usepop_back()
to remove last character, becomes much more clear and clean then.
– ifyalciner
Dec 7 '17 at 23:31
add a comment |
1
Thanks for the simplistic code. Might want to change it to "auto &" though to avoid the extra copies and get some easy performance gains.
– Millie Smith
Sep 8 '17 at 4:31
Instead of usingsubstr(...)
, usepop_back()
to remove last character, becomes much more clear and clean then.
– ifyalciner
Dec 7 '17 at 23:31
1
1
Thanks for the simplistic code. Might want to change it to "auto &" though to avoid the extra copies and get some easy performance gains.
– Millie Smith
Sep 8 '17 at 4:31
Thanks for the simplistic code. Might want to change it to "auto &" though to avoid the extra copies and get some easy performance gains.
– Millie Smith
Sep 8 '17 at 4:31
Instead of using
substr(...)
, use pop_back()
to remove last character, becomes much more clear and clean then.– ifyalciner
Dec 7 '17 at 23:31
Instead of using
substr(...)
, use pop_back()
to remove last character, becomes much more clear and clean then.– ifyalciner
Dec 7 '17 at 23:31
add a comment |
If you want to do std::cout << join(myVector, ",") << std::endl;
, you can do something like:
template <typename C, typename T> class MyJoiner
{
C &c;
T &s;
MyJoiner(C &&container, T&& sep) : c(std::forward<C>(container)), s(std::forward<T>(sep)) {}
public:
template<typename C, typename T> friend std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj);
template<typename C, typename T> friend MyJoiner<C, T> join(C &&container, T&& sep);
};
template<typename C, typename T> std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj)
{
auto i = mj.c.begin();
if (i != mj.c.end())
{
o << *i++;
while (i != mj.c.end())
{
o << mj.s << *i++;
}
}
return o;
}
template<typename C, typename T> MyJoiner<C, T> join(C &&container, T&& sep)
{
return MyJoiner<C, T>(std::forward<C>(container), std::forward<T>(sep));
}
Note, this solution does the join directly into the output stream rather than creating a secondary buffer and will work with any types that have an operator<< onto an ostream.
This also works where boost::algorithm::join()
fails, when you have a vector<char*>
instead of a vector<string>
.
add a comment |
If you want to do std::cout << join(myVector, ",") << std::endl;
, you can do something like:
template <typename C, typename T> class MyJoiner
{
C &c;
T &s;
MyJoiner(C &&container, T&& sep) : c(std::forward<C>(container)), s(std::forward<T>(sep)) {}
public:
template<typename C, typename T> friend std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj);
template<typename C, typename T> friend MyJoiner<C, T> join(C &&container, T&& sep);
};
template<typename C, typename T> std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj)
{
auto i = mj.c.begin();
if (i != mj.c.end())
{
o << *i++;
while (i != mj.c.end())
{
o << mj.s << *i++;
}
}
return o;
}
template<typename C, typename T> MyJoiner<C, T> join(C &&container, T&& sep)
{
return MyJoiner<C, T>(std::forward<C>(container), std::forward<T>(sep));
}
Note, this solution does the join directly into the output stream rather than creating a secondary buffer and will work with any types that have an operator<< onto an ostream.
This also works where boost::algorithm::join()
fails, when you have a vector<char*>
instead of a vector<string>
.
add a comment |
If you want to do std::cout << join(myVector, ",") << std::endl;
, you can do something like:
template <typename C, typename T> class MyJoiner
{
C &c;
T &s;
MyJoiner(C &&container, T&& sep) : c(std::forward<C>(container)), s(std::forward<T>(sep)) {}
public:
template<typename C, typename T> friend std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj);
template<typename C, typename T> friend MyJoiner<C, T> join(C &&container, T&& sep);
};
template<typename C, typename T> std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj)
{
auto i = mj.c.begin();
if (i != mj.c.end())
{
o << *i++;
while (i != mj.c.end())
{
o << mj.s << *i++;
}
}
return o;
}
template<typename C, typename T> MyJoiner<C, T> join(C &&container, T&& sep)
{
return MyJoiner<C, T>(std::forward<C>(container), std::forward<T>(sep));
}
Note, this solution does the join directly into the output stream rather than creating a secondary buffer and will work with any types that have an operator<< onto an ostream.
This also works where boost::algorithm::join()
fails, when you have a vector<char*>
instead of a vector<string>
.
If you want to do std::cout << join(myVector, ",") << std::endl;
, you can do something like:
template <typename C, typename T> class MyJoiner
{
C &c;
T &s;
MyJoiner(C &&container, T&& sep) : c(std::forward<C>(container)), s(std::forward<T>(sep)) {}
public:
template<typename C, typename T> friend std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj);
template<typename C, typename T> friend MyJoiner<C, T> join(C &&container, T&& sep);
};
template<typename C, typename T> std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj)
{
auto i = mj.c.begin();
if (i != mj.c.end())
{
o << *i++;
while (i != mj.c.end())
{
o << mj.s << *i++;
}
}
return o;
}
template<typename C, typename T> MyJoiner<C, T> join(C &&container, T&& sep)
{
return MyJoiner<C, T>(std::forward<C>(container), std::forward<T>(sep));
}
Note, this solution does the join directly into the output stream rather than creating a secondary buffer and will work with any types that have an operator<< onto an ostream.
This also works where boost::algorithm::join()
fails, when you have a vector<char*>
instead of a vector<string>
.
answered Jun 21 '15 at 22:59
mheymanmheyman
2,8732728
2,8732728
add a comment |
add a comment |
I like 1800's answer. However I would move the first iteration out of the loop as as the result of the if statement only changes once after the first iteration
template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
T result;
A it = begin;
if (it != end)
{
result.append(*it);
++it;
}
for( ;
it!=end;
++it)
{
result.append(t);
result.append(*it);
}
return result;
}
This can of course be reduced down to fewer statements if you like:
template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
T result;
A it = begin;
if (it != end)
result.append(*it++);
for( ; it!=end; ++it)
result.append(t).append(*it);
return result;
}
You shouldn't use post-increment for unknown iterator types. That might be expensive. (Of course, when dealing with strings, that might not make that much of a difference. But once you learn the habit...)
– sbi
Sep 16 '09 at 19:29
Post increment is fine as long as you use the tempory value that is returned. eg "result.append(*it); ++it;" is almost always as expensive as "result.append(*it++);" the second has one extra copy of the iterator.
– iain
Sep 17 '09 at 13:12
Oops I just spotted the post increment in the for loop. copy and paste error. I have fixed the post.
– iain
Sep 17 '09 at 13:20
1
@Ian: When I taught C++, I hammered into my students to use++i
except where they really neededi++
because that was the only way they wouldn't forget this when it made a difference. (It was the same with me, BTW.) They had learned Java before, where all kinds of C-isms are en vogue and it took them a few months (1 lecture +lab work per week), but in the end most of them learned the habit to use pre-increment.
– sbi
Sep 17 '09 at 14:50
1
@sbi: agreed I always default to preincrement too, the rogue postincrement came from copying someone elses for loop and changing it. In my first reply I thought you were worried about "result.append(*it++)" and not the for loop. I was a little embarrassed to see the post increment in the loop. Some people seem to follow the advice of not using post increment too far and never use it or change it even when it is appropriate. However I know now you don't fall in to this category.
– iain
Sep 18 '09 at 9:50
add a comment |
I like 1800's answer. However I would move the first iteration out of the loop as as the result of the if statement only changes once after the first iteration
template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
T result;
A it = begin;
if (it != end)
{
result.append(*it);
++it;
}
for( ;
it!=end;
++it)
{
result.append(t);
result.append(*it);
}
return result;
}
This can of course be reduced down to fewer statements if you like:
template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
T result;
A it = begin;
if (it != end)
result.append(*it++);
for( ; it!=end; ++it)
result.append(t).append(*it);
return result;
}
You shouldn't use post-increment for unknown iterator types. That might be expensive. (Of course, when dealing with strings, that might not make that much of a difference. But once you learn the habit...)
– sbi
Sep 16 '09 at 19:29
Post increment is fine as long as you use the tempory value that is returned. eg "result.append(*it); ++it;" is almost always as expensive as "result.append(*it++);" the second has one extra copy of the iterator.
– iain
Sep 17 '09 at 13:12
Oops I just spotted the post increment in the for loop. copy and paste error. I have fixed the post.
– iain
Sep 17 '09 at 13:20
1
@Ian: When I taught C++, I hammered into my students to use++i
except where they really neededi++
because that was the only way they wouldn't forget this when it made a difference. (It was the same with me, BTW.) They had learned Java before, where all kinds of C-isms are en vogue and it took them a few months (1 lecture +lab work per week), but in the end most of them learned the habit to use pre-increment.
– sbi
Sep 17 '09 at 14:50
1
@sbi: agreed I always default to preincrement too, the rogue postincrement came from copying someone elses for loop and changing it. In my first reply I thought you were worried about "result.append(*it++)" and not the for loop. I was a little embarrassed to see the post increment in the loop. Some people seem to follow the advice of not using post increment too far and never use it or change it even when it is appropriate. However I know now you don't fall in to this category.
– iain
Sep 18 '09 at 9:50
add a comment |
I like 1800's answer. However I would move the first iteration out of the loop as as the result of the if statement only changes once after the first iteration
template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
T result;
A it = begin;
if (it != end)
{
result.append(*it);
++it;
}
for( ;
it!=end;
++it)
{
result.append(t);
result.append(*it);
}
return result;
}
This can of course be reduced down to fewer statements if you like:
template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
T result;
A it = begin;
if (it != end)
result.append(*it++);
for( ; it!=end; ++it)
result.append(t).append(*it);
return result;
}
I like 1800's answer. However I would move the first iteration out of the loop as as the result of the if statement only changes once after the first iteration
template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
T result;
A it = begin;
if (it != end)
{
result.append(*it);
++it;
}
for( ;
it!=end;
++it)
{
result.append(t);
result.append(*it);
}
return result;
}
This can of course be reduced down to fewer statements if you like:
template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
T result;
A it = begin;
if (it != end)
result.append(*it++);
for( ; it!=end; ++it)
result.append(t).append(*it);
return result;
}
edited Sep 17 '09 at 13:20
answered Sep 16 '09 at 10:23
iainiain
8,98232937
8,98232937
You shouldn't use post-increment for unknown iterator types. That might be expensive. (Of course, when dealing with strings, that might not make that much of a difference. But once you learn the habit...)
– sbi
Sep 16 '09 at 19:29
Post increment is fine as long as you use the tempory value that is returned. eg "result.append(*it); ++it;" is almost always as expensive as "result.append(*it++);" the second has one extra copy of the iterator.
– iain
Sep 17 '09 at 13:12
Oops I just spotted the post increment in the for loop. copy and paste error. I have fixed the post.
– iain
Sep 17 '09 at 13:20
1
@Ian: When I taught C++, I hammered into my students to use++i
except where they really neededi++
because that was the only way they wouldn't forget this when it made a difference. (It was the same with me, BTW.) They had learned Java before, where all kinds of C-isms are en vogue and it took them a few months (1 lecture +lab work per week), but in the end most of them learned the habit to use pre-increment.
– sbi
Sep 17 '09 at 14:50
1
@sbi: agreed I always default to preincrement too, the rogue postincrement came from copying someone elses for loop and changing it. In my first reply I thought you were worried about "result.append(*it++)" and not the for loop. I was a little embarrassed to see the post increment in the loop. Some people seem to follow the advice of not using post increment too far and never use it or change it even when it is appropriate. However I know now you don't fall in to this category.
– iain
Sep 18 '09 at 9:50
add a comment |
You shouldn't use post-increment for unknown iterator types. That might be expensive. (Of course, when dealing with strings, that might not make that much of a difference. But once you learn the habit...)
– sbi
Sep 16 '09 at 19:29
Post increment is fine as long as you use the tempory value that is returned. eg "result.append(*it); ++it;" is almost always as expensive as "result.append(*it++);" the second has one extra copy of the iterator.
– iain
Sep 17 '09 at 13:12
Oops I just spotted the post increment in the for loop. copy and paste error. I have fixed the post.
– iain
Sep 17 '09 at 13:20
1
@Ian: When I taught C++, I hammered into my students to use++i
except where they really neededi++
because that was the only way they wouldn't forget this when it made a difference. (It was the same with me, BTW.) They had learned Java before, where all kinds of C-isms are en vogue and it took them a few months (1 lecture +lab work per week), but in the end most of them learned the habit to use pre-increment.
– sbi
Sep 17 '09 at 14:50
1
@sbi: agreed I always default to preincrement too, the rogue postincrement came from copying someone elses for loop and changing it. In my first reply I thought you were worried about "result.append(*it++)" and not the for loop. I was a little embarrassed to see the post increment in the loop. Some people seem to follow the advice of not using post increment too far and never use it or change it even when it is appropriate. However I know now you don't fall in to this category.
– iain
Sep 18 '09 at 9:50
You shouldn't use post-increment for unknown iterator types. That might be expensive. (Of course, when dealing with strings, that might not make that much of a difference. But once you learn the habit...)
– sbi
Sep 16 '09 at 19:29
You shouldn't use post-increment for unknown iterator types. That might be expensive. (Of course, when dealing with strings, that might not make that much of a difference. But once you learn the habit...)
– sbi
Sep 16 '09 at 19:29
Post increment is fine as long as you use the tempory value that is returned. eg "result.append(*it); ++it;" is almost always as expensive as "result.append(*it++);" the second has one extra copy of the iterator.
– iain
Sep 17 '09 at 13:12
Post increment is fine as long as you use the tempory value that is returned. eg "result.append(*it); ++it;" is almost always as expensive as "result.append(*it++);" the second has one extra copy of the iterator.
– iain
Sep 17 '09 at 13:12
Oops I just spotted the post increment in the for loop. copy and paste error. I have fixed the post.
– iain
Sep 17 '09 at 13:20
Oops I just spotted the post increment in the for loop. copy and paste error. I have fixed the post.
– iain
Sep 17 '09 at 13:20
1
1
@Ian: When I taught C++, I hammered into my students to use
++i
except where they really needed i++
because that was the only way they wouldn't forget this when it made a difference. (It was the same with me, BTW.) They had learned Java before, where all kinds of C-isms are en vogue and it took them a few months (1 lecture +lab work per week), but in the end most of them learned the habit to use pre-increment.– sbi
Sep 17 '09 at 14:50
@Ian: When I taught C++, I hammered into my students to use
++i
except where they really needed i++
because that was the only way they wouldn't forget this when it made a difference. (It was the same with me, BTW.) They had learned Java before, where all kinds of C-isms are en vogue and it took them a few months (1 lecture +lab work per week), but in the end most of them learned the habit to use pre-increment.– sbi
Sep 17 '09 at 14:50
1
1
@sbi: agreed I always default to preincrement too, the rogue postincrement came from copying someone elses for loop and changing it. In my first reply I thought you were worried about "result.append(*it++)" and not the for loop. I was a little embarrassed to see the post increment in the loop. Some people seem to follow the advice of not using post increment too far and never use it or change it even when it is appropriate. However I know now you don't fall in to this category.
– iain
Sep 18 '09 at 9:50
@sbi: agreed I always default to preincrement too, the rogue postincrement came from copying someone elses for loop and changing it. In my first reply I thought you were worried about "result.append(*it++)" and not the for loop. I was a little embarrassed to see the post increment in the loop. Some people seem to follow the advice of not using post increment too far and never use it or change it even when it is appropriate. However I know now you don't fall in to this category.
– iain
Sep 18 '09 at 9:50
add a comment |
There are some interesting attempts at providing an elegant solution to the problem. I had an idea to use templated streams to effectively answer the OP's original dilemma. Though this is an old post, I'm hoping future users who stumble upon this will find my solution beneficial.
First, some answers (including the accepted answer) do not promote re-usability. Since C++ doesn't provide an elegant way to join strings in the standard library (that I have seen), it becomes important to create one that is flexible and reusable. Here's my shot at it:
// Replace with your namespace //
namespace my {
// Templated join which can be used on any combination of streams, iterators and base types //
template <typename TStream, typename TIter, typename TSeperator>
TStream& join(TStream& stream, TIter begin, TIter end, TSeperator seperator) {
// A flag which, when true, has next iteration prepend our seperator to the stream //
bool sep = false;
// Begin iterating through our list //
for (TIter i = begin; i != end; ++i) {
// If we need to prepend a seperator, do it //
if (sep) stream << seperator;
// Stream the next value held by our iterator //
stream << *i;
// Flag that next loops needs a seperator //
sep = true;
}
// As a convenience, we return a reference to the passed stream //
return stream;
}
}
Now to use this, you could simply do something like the following:
// Load some data //
std::vector<int> params;
params.push_back(1);
params.push_back(2);
params.push_back(3);
params.push_back(4);
// Store and print our results to standard out //
std::stringstream param_stream;
std::cout << my::join(param_stream, params.begin(), params.end(), ",").str() << std::endl;
// A quick and dirty way to print directly to standard out //
my::join(std::cout, params.begin(), params.end(), ",") << std::endl;
Note how the use of streams makes this solution incredibly flexible as we can store our result in a stringstream to reclaim it later, or we can write directly to the standard out, a file, or even to a network connection implemented as a stream. The type being printed must simply be iteratable and compatible with the source stream. STL provides various streams which are compatible with a large range of types. So you could really go to town with this. Off the top of my head, your vector can be of int, float, double, string, unsigned int, SomeObject*, and more.
add a comment |
There are some interesting attempts at providing an elegant solution to the problem. I had an idea to use templated streams to effectively answer the OP's original dilemma. Though this is an old post, I'm hoping future users who stumble upon this will find my solution beneficial.
First, some answers (including the accepted answer) do not promote re-usability. Since C++ doesn't provide an elegant way to join strings in the standard library (that I have seen), it becomes important to create one that is flexible and reusable. Here's my shot at it:
// Replace with your namespace //
namespace my {
// Templated join which can be used on any combination of streams, iterators and base types //
template <typename TStream, typename TIter, typename TSeperator>
TStream& join(TStream& stream, TIter begin, TIter end, TSeperator seperator) {
// A flag which, when true, has next iteration prepend our seperator to the stream //
bool sep = false;
// Begin iterating through our list //
for (TIter i = begin; i != end; ++i) {
// If we need to prepend a seperator, do it //
if (sep) stream << seperator;
// Stream the next value held by our iterator //
stream << *i;
// Flag that next loops needs a seperator //
sep = true;
}
// As a convenience, we return a reference to the passed stream //
return stream;
}
}
Now to use this, you could simply do something like the following:
// Load some data //
std::vector<int> params;
params.push_back(1);
params.push_back(2);
params.push_back(3);
params.push_back(4);
// Store and print our results to standard out //
std::stringstream param_stream;
std::cout << my::join(param_stream, params.begin(), params.end(), ",").str() << std::endl;
// A quick and dirty way to print directly to standard out //
my::join(std::cout, params.begin(), params.end(), ",") << std::endl;
Note how the use of streams makes this solution incredibly flexible as we can store our result in a stringstream to reclaim it later, or we can write directly to the standard out, a file, or even to a network connection implemented as a stream. The type being printed must simply be iteratable and compatible with the source stream. STL provides various streams which are compatible with a large range of types. So you could really go to town with this. Off the top of my head, your vector can be of int, float, double, string, unsigned int, SomeObject*, and more.
add a comment |
There are some interesting attempts at providing an elegant solution to the problem. I had an idea to use templated streams to effectively answer the OP's original dilemma. Though this is an old post, I'm hoping future users who stumble upon this will find my solution beneficial.
First, some answers (including the accepted answer) do not promote re-usability. Since C++ doesn't provide an elegant way to join strings in the standard library (that I have seen), it becomes important to create one that is flexible and reusable. Here's my shot at it:
// Replace with your namespace //
namespace my {
// Templated join which can be used on any combination of streams, iterators and base types //
template <typename TStream, typename TIter, typename TSeperator>
TStream& join(TStream& stream, TIter begin, TIter end, TSeperator seperator) {
// A flag which, when true, has next iteration prepend our seperator to the stream //
bool sep = false;
// Begin iterating through our list //
for (TIter i = begin; i != end; ++i) {
// If we need to prepend a seperator, do it //
if (sep) stream << seperator;
// Stream the next value held by our iterator //
stream << *i;
// Flag that next loops needs a seperator //
sep = true;
}
// As a convenience, we return a reference to the passed stream //
return stream;
}
}
Now to use this, you could simply do something like the following:
// Load some data //
std::vector<int> params;
params.push_back(1);
params.push_back(2);
params.push_back(3);
params.push_back(4);
// Store and print our results to standard out //
std::stringstream param_stream;
std::cout << my::join(param_stream, params.begin(), params.end(), ",").str() << std::endl;
// A quick and dirty way to print directly to standard out //
my::join(std::cout, params.begin(), params.end(), ",") << std::endl;
Note how the use of streams makes this solution incredibly flexible as we can store our result in a stringstream to reclaim it later, or we can write directly to the standard out, a file, or even to a network connection implemented as a stream. The type being printed must simply be iteratable and compatible with the source stream. STL provides various streams which are compatible with a large range of types. So you could really go to town with this. Off the top of my head, your vector can be of int, float, double, string, unsigned int, SomeObject*, and more.
There are some interesting attempts at providing an elegant solution to the problem. I had an idea to use templated streams to effectively answer the OP's original dilemma. Though this is an old post, I'm hoping future users who stumble upon this will find my solution beneficial.
First, some answers (including the accepted answer) do not promote re-usability. Since C++ doesn't provide an elegant way to join strings in the standard library (that I have seen), it becomes important to create one that is flexible and reusable. Here's my shot at it:
// Replace with your namespace //
namespace my {
// Templated join which can be used on any combination of streams, iterators and base types //
template <typename TStream, typename TIter, typename TSeperator>
TStream& join(TStream& stream, TIter begin, TIter end, TSeperator seperator) {
// A flag which, when true, has next iteration prepend our seperator to the stream //
bool sep = false;
// Begin iterating through our list //
for (TIter i = begin; i != end; ++i) {
// If we need to prepend a seperator, do it //
if (sep) stream << seperator;
// Stream the next value held by our iterator //
stream << *i;
// Flag that next loops needs a seperator //
sep = true;
}
// As a convenience, we return a reference to the passed stream //
return stream;
}
}
Now to use this, you could simply do something like the following:
// Load some data //
std::vector<int> params;
params.push_back(1);
params.push_back(2);
params.push_back(3);
params.push_back(4);
// Store and print our results to standard out //
std::stringstream param_stream;
std::cout << my::join(param_stream, params.begin(), params.end(), ",").str() << std::endl;
// A quick and dirty way to print directly to standard out //
my::join(std::cout, params.begin(), params.end(), ",") << std::endl;
Note how the use of streams makes this solution incredibly flexible as we can store our result in a stringstream to reclaim it later, or we can write directly to the standard out, a file, or even to a network connection implemented as a stream. The type being printed must simply be iteratable and compatible with the source stream. STL provides various streams which are compatible with a large range of types. So you could really go to town with this. Off the top of my head, your vector can be of int, float, double, string, unsigned int, SomeObject*, and more.
answered Jul 13 '13 at 19:08
David PetersonDavid Peterson
643616
643616
add a comment |
add a comment |
string s;
for (auto i : v)
s += (s.empty() ? "" : ",") + to_string(i);
5
Welcome to Stack Overflow! While this code could solve the problem, it is best to add elaboration and explain how it works for people who might not understand this piece of code.
– paper1111
Jan 23 at 10:18
The current top answer is not much more elaborate, and this is the smallest/cleanest working answer. Not as efficient asstd::stringstream
for large arrays becausestringstream
will be able to allocate memory optimistically, leading to O(n.log(n)) performance instead of O(n²) for an array of sizen
for this answer. Alsostringstream
might not build temporary strings forto_string(i)
.
– aberaud
Feb 17 at 20:10
add a comment |
string s;
for (auto i : v)
s += (s.empty() ? "" : ",") + to_string(i);
5
Welcome to Stack Overflow! While this code could solve the problem, it is best to add elaboration and explain how it works for people who might not understand this piece of code.
– paper1111
Jan 23 at 10:18
The current top answer is not much more elaborate, and this is the smallest/cleanest working answer. Not as efficient asstd::stringstream
for large arrays becausestringstream
will be able to allocate memory optimistically, leading to O(n.log(n)) performance instead of O(n²) for an array of sizen
for this answer. Alsostringstream
might not build temporary strings forto_string(i)
.
– aberaud
Feb 17 at 20:10
add a comment |
string s;
for (auto i : v)
s += (s.empty() ? "" : ",") + to_string(i);
string s;
for (auto i : v)
s += (s.empty() ? "" : ",") + to_string(i);
answered Jan 23 at 10:17
chenfy27chenfy27
211
211
5
Welcome to Stack Overflow! While this code could solve the problem, it is best to add elaboration and explain how it works for people who might not understand this piece of code.
– paper1111
Jan 23 at 10:18
The current top answer is not much more elaborate, and this is the smallest/cleanest working answer. Not as efficient asstd::stringstream
for large arrays becausestringstream
will be able to allocate memory optimistically, leading to O(n.log(n)) performance instead of O(n²) for an array of sizen
for this answer. Alsostringstream
might not build temporary strings forto_string(i)
.
– aberaud
Feb 17 at 20:10
add a comment |
5
Welcome to Stack Overflow! While this code could solve the problem, it is best to add elaboration and explain how it works for people who might not understand this piece of code.
– paper1111
Jan 23 at 10:18
The current top answer is not much more elaborate, and this is the smallest/cleanest working answer. Not as efficient asstd::stringstream
for large arrays becausestringstream
will be able to allocate memory optimistically, leading to O(n.log(n)) performance instead of O(n²) for an array of sizen
for this answer. Alsostringstream
might not build temporary strings forto_string(i)
.
– aberaud
Feb 17 at 20:10
5
5
Welcome to Stack Overflow! While this code could solve the problem, it is best to add elaboration and explain how it works for people who might not understand this piece of code.
– paper1111
Jan 23 at 10:18
Welcome to Stack Overflow! While this code could solve the problem, it is best to add elaboration and explain how it works for people who might not understand this piece of code.
– paper1111
Jan 23 at 10:18
The current top answer is not much more elaborate, and this is the smallest/cleanest working answer. Not as efficient as
std::stringstream
for large arrays because stringstream
will be able to allocate memory optimistically, leading to O(n.log(n)) performance instead of O(n²) for an array of size n
for this answer. Also stringstream
might not build temporary strings for to_string(i)
.– aberaud
Feb 17 at 20:10
The current top answer is not much more elaborate, and this is the smallest/cleanest working answer. Not as efficient as
std::stringstream
for large arrays because stringstream
will be able to allocate memory optimistically, leading to O(n.log(n)) performance instead of O(n²) for an array of size n
for this answer. Also stringstream
might not build temporary strings for to_string(i)
.– aberaud
Feb 17 at 20:10
add a comment |
I've created an helper header file to add an extended join support.
Just add the code below to your general header file and include it when needed.
Usage Examples:
/* An example for a mapping function. */
ostream&
map_numbers(ostream& os, const void* payload, generic_primitive data)
{
static string names = {"Zero", "One", "Two", "Three", "Four"};
os << names[data.as_int];
const string* post = reinterpret_cast<const string*>(payload);
if (post) {
os << " " << *post;
}
return os;
}
int main() {
int arr = {0,1,2,3,4};
vector<int> vec(arr, arr + 5);
cout << vec << endl; /* Outputs: '0 1 2 3 4' */
cout << join(vec.begin(), vec.end()) << endl; /* Outputs: '0 1 2 3 4' */
cout << join(vec.begin(), vec.begin() + 2) << endl; /* Outputs: '0 1 2' */
cout << join(vec.begin(), vec.end(), ", ") << endl; /* Outputs: '0, 1, 2, 3, 4' */
cout << join(vec.begin(), vec.end(), ", ", map_numbers) << endl; /* Outputs: 'Zero, One, Two, Three, Four' */
string post = "Mississippi";
cout << join(vec.begin() + 1, vec.end(), ", ", map_numbers, &post) << endl; /* Outputs: 'One Mississippi, Two mississippi, Three mississippi, Four mississippi' */
return 0;
}
The code behind the scene:
#include <iostream>
#include <vector>
#include <list>
#include <set>
#include <unordered_set>
using namespace std;
#define GENERIC_PRIMITIVE_CLASS_BUILDER(T) generic_primitive(const T& v) { value.as_##T = v; }
#define GENERIC_PRIMITIVE_TYPE_BUILDER(T) T as_##T;
typedef void* ptr;
/** A union that could contain a primitive or void*,
* used for generic function pointers.
* TODO: add more primitive types as needed.
*/
struct generic_primitive {
GENERIC_PRIMITIVE_CLASS_BUILDER(int);
GENERIC_PRIMITIVE_CLASS_BUILDER(ptr);
union {
GENERIC_PRIMITIVE_TYPE_BUILDER(int);
GENERIC_PRIMITIVE_TYPE_BUILDER(ptr);
};
};
typedef ostream& (*mapping_funct_t)(ostream&, const void*, generic_primitive);
template<typename T>
class Join {
public:
Join(const T& begin, const T& end,
const string& separator = " ",
mapping_funct_t mapping = 0,
const void* payload = 0):
m_begin(begin),
m_end(end),
m_separator(separator),
m_mapping(mapping),
m_payload(payload) {}
ostream&
apply(ostream& os) const
{
T begin = m_begin;
T end = m_end;
if (begin != end)
if (m_mapping) {
m_mapping(os, m_payload, *begin++);
} else {
os << *begin++;
}
while (begin != end) {
os << m_separator;
if (m_mapping) {
m_mapping(os, m_payload, *begin++);
} else {
os << *begin++;
}
}
return os;
}
private:
const T& m_begin;
const T& m_end;
const string m_separator;
const mapping_funct_t m_mapping;
const void* m_payload;
};
template <typename T>
Join<T>
join(const T& begin, const T& end,
const string& separator = " ",
ostream& (*mapping)(ostream&, const void*, generic_primitive) = 0,
const void* payload = 0)
{
return Join<T>(begin, end, separator, mapping, payload);
}
template<typename T>
ostream&
operator<<(ostream& os, const vector<T>& vec) {
return join(vec.begin(), vec.end()).apply(os);
}
template<typename T>
ostream&
operator<<(ostream& os, const list<T>& lst) {
return join(lst.begin(), lst.end()).apply(os);
}
template<typename T>
ostream&
operator<<(ostream& os, const set<T>& s) {
return join(s.begin(), s.end()).apply(os);
}
template<typename T>
ostream&
operator<<(ostream& os, const Join<T>& vec) {
return vec.apply(os);
}
add a comment |
I've created an helper header file to add an extended join support.
Just add the code below to your general header file and include it when needed.
Usage Examples:
/* An example for a mapping function. */
ostream&
map_numbers(ostream& os, const void* payload, generic_primitive data)
{
static string names = {"Zero", "One", "Two", "Three", "Four"};
os << names[data.as_int];
const string* post = reinterpret_cast<const string*>(payload);
if (post) {
os << " " << *post;
}
return os;
}
int main() {
int arr = {0,1,2,3,4};
vector<int> vec(arr, arr + 5);
cout << vec << endl; /* Outputs: '0 1 2 3 4' */
cout << join(vec.begin(), vec.end()) << endl; /* Outputs: '0 1 2 3 4' */
cout << join(vec.begin(), vec.begin() + 2) << endl; /* Outputs: '0 1 2' */
cout << join(vec.begin(), vec.end(), ", ") << endl; /* Outputs: '0, 1, 2, 3, 4' */
cout << join(vec.begin(), vec.end(), ", ", map_numbers) << endl; /* Outputs: 'Zero, One, Two, Three, Four' */
string post = "Mississippi";
cout << join(vec.begin() + 1, vec.end(), ", ", map_numbers, &post) << endl; /* Outputs: 'One Mississippi, Two mississippi, Three mississippi, Four mississippi' */
return 0;
}
The code behind the scene:
#include <iostream>
#include <vector>
#include <list>
#include <set>
#include <unordered_set>
using namespace std;
#define GENERIC_PRIMITIVE_CLASS_BUILDER(T) generic_primitive(const T& v) { value.as_##T = v; }
#define GENERIC_PRIMITIVE_TYPE_BUILDER(T) T as_##T;
typedef void* ptr;
/** A union that could contain a primitive or void*,
* used for generic function pointers.
* TODO: add more primitive types as needed.
*/
struct generic_primitive {
GENERIC_PRIMITIVE_CLASS_BUILDER(int);
GENERIC_PRIMITIVE_CLASS_BUILDER(ptr);
union {
GENERIC_PRIMITIVE_TYPE_BUILDER(int);
GENERIC_PRIMITIVE_TYPE_BUILDER(ptr);
};
};
typedef ostream& (*mapping_funct_t)(ostream&, const void*, generic_primitive);
template<typename T>
class Join {
public:
Join(const T& begin, const T& end,
const string& separator = " ",
mapping_funct_t mapping = 0,
const void* payload = 0):
m_begin(begin),
m_end(end),
m_separator(separator),
m_mapping(mapping),
m_payload(payload) {}
ostream&
apply(ostream& os) const
{
T begin = m_begin;
T end = m_end;
if (begin != end)
if (m_mapping) {
m_mapping(os, m_payload, *begin++);
} else {
os << *begin++;
}
while (begin != end) {
os << m_separator;
if (m_mapping) {
m_mapping(os, m_payload, *begin++);
} else {
os << *begin++;
}
}
return os;
}
private:
const T& m_begin;
const T& m_end;
const string m_separator;
const mapping_funct_t m_mapping;
const void* m_payload;
};
template <typename T>
Join<T>
join(const T& begin, const T& end,
const string& separator = " ",
ostream& (*mapping)(ostream&, const void*, generic_primitive) = 0,
const void* payload = 0)
{
return Join<T>(begin, end, separator, mapping, payload);
}
template<typename T>
ostream&
operator<<(ostream& os, const vector<T>& vec) {
return join(vec.begin(), vec.end()).apply(os);
}
template<typename T>
ostream&
operator<<(ostream& os, const list<T>& lst) {
return join(lst.begin(), lst.end()).apply(os);
}
template<typename T>
ostream&
operator<<(ostream& os, const set<T>& s) {
return join(s.begin(), s.end()).apply(os);
}
template<typename T>
ostream&
operator<<(ostream& os, const Join<T>& vec) {
return vec.apply(os);
}
add a comment |
I've created an helper header file to add an extended join support.
Just add the code below to your general header file and include it when needed.
Usage Examples:
/* An example for a mapping function. */
ostream&
map_numbers(ostream& os, const void* payload, generic_primitive data)
{
static string names = {"Zero", "One", "Two", "Three", "Four"};
os << names[data.as_int];
const string* post = reinterpret_cast<const string*>(payload);
if (post) {
os << " " << *post;
}
return os;
}
int main() {
int arr = {0,1,2,3,4};
vector<int> vec(arr, arr + 5);
cout << vec << endl; /* Outputs: '0 1 2 3 4' */
cout << join(vec.begin(), vec.end()) << endl; /* Outputs: '0 1 2 3 4' */
cout << join(vec.begin(), vec.begin() + 2) << endl; /* Outputs: '0 1 2' */
cout << join(vec.begin(), vec.end(), ", ") << endl; /* Outputs: '0, 1, 2, 3, 4' */
cout << join(vec.begin(), vec.end(), ", ", map_numbers) << endl; /* Outputs: 'Zero, One, Two, Three, Four' */
string post = "Mississippi";
cout << join(vec.begin() + 1, vec.end(), ", ", map_numbers, &post) << endl; /* Outputs: 'One Mississippi, Two mississippi, Three mississippi, Four mississippi' */
return 0;
}
The code behind the scene:
#include <iostream>
#include <vector>
#include <list>
#include <set>
#include <unordered_set>
using namespace std;
#define GENERIC_PRIMITIVE_CLASS_BUILDER(T) generic_primitive(const T& v) { value.as_##T = v; }
#define GENERIC_PRIMITIVE_TYPE_BUILDER(T) T as_##T;
typedef void* ptr;
/** A union that could contain a primitive or void*,
* used for generic function pointers.
* TODO: add more primitive types as needed.
*/
struct generic_primitive {
GENERIC_PRIMITIVE_CLASS_BUILDER(int);
GENERIC_PRIMITIVE_CLASS_BUILDER(ptr);
union {
GENERIC_PRIMITIVE_TYPE_BUILDER(int);
GENERIC_PRIMITIVE_TYPE_BUILDER(ptr);
};
};
typedef ostream& (*mapping_funct_t)(ostream&, const void*, generic_primitive);
template<typename T>
class Join {
public:
Join(const T& begin, const T& end,
const string& separator = " ",
mapping_funct_t mapping = 0,
const void* payload = 0):
m_begin(begin),
m_end(end),
m_separator(separator),
m_mapping(mapping),
m_payload(payload) {}
ostream&
apply(ostream& os) const
{
T begin = m_begin;
T end = m_end;
if (begin != end)
if (m_mapping) {
m_mapping(os, m_payload, *begin++);
} else {
os << *begin++;
}
while (begin != end) {
os << m_separator;
if (m_mapping) {
m_mapping(os, m_payload, *begin++);
} else {
os << *begin++;
}
}
return os;
}
private:
const T& m_begin;
const T& m_end;
const string m_separator;
const mapping_funct_t m_mapping;
const void* m_payload;
};
template <typename T>
Join<T>
join(const T& begin, const T& end,
const string& separator = " ",
ostream& (*mapping)(ostream&, const void*, generic_primitive) = 0,
const void* payload = 0)
{
return Join<T>(begin, end, separator, mapping, payload);
}
template<typename T>
ostream&
operator<<(ostream& os, const vector<T>& vec) {
return join(vec.begin(), vec.end()).apply(os);
}
template<typename T>
ostream&
operator<<(ostream& os, const list<T>& lst) {
return join(lst.begin(), lst.end()).apply(os);
}
template<typename T>
ostream&
operator<<(ostream& os, const set<T>& s) {
return join(s.begin(), s.end()).apply(os);
}
template<typename T>
ostream&
operator<<(ostream& os, const Join<T>& vec) {
return vec.apply(os);
}
I've created an helper header file to add an extended join support.
Just add the code below to your general header file and include it when needed.
Usage Examples:
/* An example for a mapping function. */
ostream&
map_numbers(ostream& os, const void* payload, generic_primitive data)
{
static string names = {"Zero", "One", "Two", "Three", "Four"};
os << names[data.as_int];
const string* post = reinterpret_cast<const string*>(payload);
if (post) {
os << " " << *post;
}
return os;
}
int main() {
int arr = {0,1,2,3,4};
vector<int> vec(arr, arr + 5);
cout << vec << endl; /* Outputs: '0 1 2 3 4' */
cout << join(vec.begin(), vec.end()) << endl; /* Outputs: '0 1 2 3 4' */
cout << join(vec.begin(), vec.begin() + 2) << endl; /* Outputs: '0 1 2' */
cout << join(vec.begin(), vec.end(), ", ") << endl; /* Outputs: '0, 1, 2, 3, 4' */
cout << join(vec.begin(), vec.end(), ", ", map_numbers) << endl; /* Outputs: 'Zero, One, Two, Three, Four' */
string post = "Mississippi";
cout << join(vec.begin() + 1, vec.end(), ", ", map_numbers, &post) << endl; /* Outputs: 'One Mississippi, Two mississippi, Three mississippi, Four mississippi' */
return 0;
}
The code behind the scene:
#include <iostream>
#include <vector>
#include <list>
#include <set>
#include <unordered_set>
using namespace std;
#define GENERIC_PRIMITIVE_CLASS_BUILDER(T) generic_primitive(const T& v) { value.as_##T = v; }
#define GENERIC_PRIMITIVE_TYPE_BUILDER(T) T as_##T;
typedef void* ptr;
/** A union that could contain a primitive or void*,
* used for generic function pointers.
* TODO: add more primitive types as needed.
*/
struct generic_primitive {
GENERIC_PRIMITIVE_CLASS_BUILDER(int);
GENERIC_PRIMITIVE_CLASS_BUILDER(ptr);
union {
GENERIC_PRIMITIVE_TYPE_BUILDER(int);
GENERIC_PRIMITIVE_TYPE_BUILDER(ptr);
};
};
typedef ostream& (*mapping_funct_t)(ostream&, const void*, generic_primitive);
template<typename T>
class Join {
public:
Join(const T& begin, const T& end,
const string& separator = " ",
mapping_funct_t mapping = 0,
const void* payload = 0):
m_begin(begin),
m_end(end),
m_separator(separator),
m_mapping(mapping),
m_payload(payload) {}
ostream&
apply(ostream& os) const
{
T begin = m_begin;
T end = m_end;
if (begin != end)
if (m_mapping) {
m_mapping(os, m_payload, *begin++);
} else {
os << *begin++;
}
while (begin != end) {
os << m_separator;
if (m_mapping) {
m_mapping(os, m_payload, *begin++);
} else {
os << *begin++;
}
}
return os;
}
private:
const T& m_begin;
const T& m_end;
const string m_separator;
const mapping_funct_t m_mapping;
const void* m_payload;
};
template <typename T>
Join<T>
join(const T& begin, const T& end,
const string& separator = " ",
ostream& (*mapping)(ostream&, const void*, generic_primitive) = 0,
const void* payload = 0)
{
return Join<T>(begin, end, separator, mapping, payload);
}
template<typename T>
ostream&
operator<<(ostream& os, const vector<T>& vec) {
return join(vec.begin(), vec.end()).apply(os);
}
template<typename T>
ostream&
operator<<(ostream& os, const list<T>& lst) {
return join(lst.begin(), lst.end()).apply(os);
}
template<typename T>
ostream&
operator<<(ostream& os, const set<T>& s) {
return join(s.begin(), s.end()).apply(os);
}
template<typename T>
ostream&
operator<<(ostream& os, const Join<T>& vec) {
return vec.apply(os);
}
edited Nov 28 '16 at 19:06
answered Nov 28 '16 at 18:54
Maor GaonMaor Gaon
112
112
add a comment |
add a comment |
Here's a generic C++11 solution that will let you do
int main() {
vector<int> v {1,2,3};
cout << join(v, ", ") << endl;
string s = join(v, '+').str();
}
The code is:
template<typename Iterable, typename Sep>
class Joiner {
const Iterable& i_;
const Sep& s_;
public:
Joiner(const Iterable& i, const Sep& s) : i_(i), s_(s) {}
std::string str() const {std::stringstream ss; ss << *this; return ss.str();}
template<typename I, typename S> friend std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& j);
};
template<typename I, typename S>
std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& j) {
auto elem = j.i_.begin();
if (elem != j.i_.end()) {
os << *elem;
++elem;
while (elem != j.i_.end()) {
os << j.s_ << *elem;
++elem;
}
}
return os;
}
template<typename I, typename S>
inline Joiner<I,S> join(const I& i, const S& s) {return Joiner<I,S>(i, s);}
add a comment |
Here's a generic C++11 solution that will let you do
int main() {
vector<int> v {1,2,3};
cout << join(v, ", ") << endl;
string s = join(v, '+').str();
}
The code is:
template<typename Iterable, typename Sep>
class Joiner {
const Iterable& i_;
const Sep& s_;
public:
Joiner(const Iterable& i, const Sep& s) : i_(i), s_(s) {}
std::string str() const {std::stringstream ss; ss << *this; return ss.str();}
template<typename I, typename S> friend std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& j);
};
template<typename I, typename S>
std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& j) {
auto elem = j.i_.begin();
if (elem != j.i_.end()) {
os << *elem;
++elem;
while (elem != j.i_.end()) {
os << j.s_ << *elem;
++elem;
}
}
return os;
}
template<typename I, typename S>
inline Joiner<I,S> join(const I& i, const S& s) {return Joiner<I,S>(i, s);}
add a comment |
Here's a generic C++11 solution that will let you do
int main() {
vector<int> v {1,2,3};
cout << join(v, ", ") << endl;
string s = join(v, '+').str();
}
The code is:
template<typename Iterable, typename Sep>
class Joiner {
const Iterable& i_;
const Sep& s_;
public:
Joiner(const Iterable& i, const Sep& s) : i_(i), s_(s) {}
std::string str() const {std::stringstream ss; ss << *this; return ss.str();}
template<typename I, typename S> friend std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& j);
};
template<typename I, typename S>
std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& j) {
auto elem = j.i_.begin();
if (elem != j.i_.end()) {
os << *elem;
++elem;
while (elem != j.i_.end()) {
os << j.s_ << *elem;
++elem;
}
}
return os;
}
template<typename I, typename S>
inline Joiner<I,S> join(const I& i, const S& s) {return Joiner<I,S>(i, s);}
Here's a generic C++11 solution that will let you do
int main() {
vector<int> v {1,2,3};
cout << join(v, ", ") << endl;
string s = join(v, '+').str();
}
The code is:
template<typename Iterable, typename Sep>
class Joiner {
const Iterable& i_;
const Sep& s_;
public:
Joiner(const Iterable& i, const Sep& s) : i_(i), s_(s) {}
std::string str() const {std::stringstream ss; ss << *this; return ss.str();}
template<typename I, typename S> friend std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& j);
};
template<typename I, typename S>
std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& j) {
auto elem = j.i_.begin();
if (elem != j.i_.end()) {
os << *elem;
++elem;
while (elem != j.i_.end()) {
os << j.s_ << *elem;
++elem;
}
}
return os;
}
template<typename I, typename S>
inline Joiner<I,S> join(const I& i, const S& s) {return Joiner<I,S>(i, s);}
edited Apr 11 '17 at 0:26
answered Apr 10 '17 at 0:05
n.cailloun.caillou
716413
716413
add a comment |
add a comment |
The following is a simple and practical way to convert elements in a vector
to a string
:
std::string join(const std::vector<int>& numbers, const std::string& delimiter = ",") {
std::ostringstream result;
for (const auto number : numbers) {
if (result.tellp() > 0) { // not first round
result << delimiter;
}
result << number;
}
return result.str();
}
You need to #include <sstream>
for ostringstream
.
add a comment |
The following is a simple and practical way to convert elements in a vector
to a string
:
std::string join(const std::vector<int>& numbers, const std::string& delimiter = ",") {
std::ostringstream result;
for (const auto number : numbers) {
if (result.tellp() > 0) { // not first round
result << delimiter;
}
result << number;
}
return result.str();
}
You need to #include <sstream>
for ostringstream
.
add a comment |
The following is a simple and practical way to convert elements in a vector
to a string
:
std::string join(const std::vector<int>& numbers, const std::string& delimiter = ",") {
std::ostringstream result;
for (const auto number : numbers) {
if (result.tellp() > 0) { // not first round
result << delimiter;
}
result << number;
}
return result.str();
}
You need to #include <sstream>
for ostringstream
.
The following is a simple and practical way to convert elements in a vector
to a string
:
std::string join(const std::vector<int>& numbers, const std::string& delimiter = ",") {
std::ostringstream result;
for (const auto number : numbers) {
if (result.tellp() > 0) { // not first round
result << delimiter;
}
result << number;
}
return result.str();
}
You need to #include <sstream>
for ostringstream
.
edited Jun 16 '18 at 9:31
answered Jun 11 '18 at 21:11
mrtsmrts
5,10333742
5,10333742
add a comment |
add a comment |
as @capone did ,
std::string join(const std::vector<std::string> &str_list ,
const std::string &delim=" ")
{
if(str_list.size() == 0) return "" ;
return std::accumulate( str_list.cbegin() + 1,
str_list.cend(),
str_list.at(0) ,
[&delim](const std::string &a , const std::string &b)
{
return a + delim + b ;
} ) ;
}
template <typename ST , typename TT>
std::vector<TT> map(TT (*op)(ST) , const vector<ST> &ori_vec)
{
vector<TT> rst ;
std::transform(ori_vec.cbegin() ,
ori_vec.cend() , back_inserter(rst) ,
[&op](const ST& val){ return op(val) ;} ) ;
return rst ;
}
Then we can call like following :
int main(int argc , char *argv)
{
vector<int> int_vec = {1,2,3,4} ;
vector<string> str_vec = map<int,string>(to_string, int_vec) ;
cout << join(str_vec) << endl ;
return 0 ;
}
just like python :
>>> " ".join( map(str, [1,2,3,4]) )
add a comment |
as @capone did ,
std::string join(const std::vector<std::string> &str_list ,
const std::string &delim=" ")
{
if(str_list.size() == 0) return "" ;
return std::accumulate( str_list.cbegin() + 1,
str_list.cend(),
str_list.at(0) ,
[&delim](const std::string &a , const std::string &b)
{
return a + delim + b ;
} ) ;
}
template <typename ST , typename TT>
std::vector<TT> map(TT (*op)(ST) , const vector<ST> &ori_vec)
{
vector<TT> rst ;
std::transform(ori_vec.cbegin() ,
ori_vec.cend() , back_inserter(rst) ,
[&op](const ST& val){ return op(val) ;} ) ;
return rst ;
}
Then we can call like following :
int main(int argc , char *argv)
{
vector<int> int_vec = {1,2,3,4} ;
vector<string> str_vec = map<int,string>(to_string, int_vec) ;
cout << join(str_vec) << endl ;
return 0 ;
}
just like python :
>>> " ".join( map(str, [1,2,3,4]) )
add a comment |
as @capone did ,
std::string join(const std::vector<std::string> &str_list ,
const std::string &delim=" ")
{
if(str_list.size() == 0) return "" ;
return std::accumulate( str_list.cbegin() + 1,
str_list.cend(),
str_list.at(0) ,
[&delim](const std::string &a , const std::string &b)
{
return a + delim + b ;
} ) ;
}
template <typename ST , typename TT>
std::vector<TT> map(TT (*op)(ST) , const vector<ST> &ori_vec)
{
vector<TT> rst ;
std::transform(ori_vec.cbegin() ,
ori_vec.cend() , back_inserter(rst) ,
[&op](const ST& val){ return op(val) ;} ) ;
return rst ;
}
Then we can call like following :
int main(int argc , char *argv)
{
vector<int> int_vec = {1,2,3,4} ;
vector<string> str_vec = map<int,string>(to_string, int_vec) ;
cout << join(str_vec) << endl ;
return 0 ;
}
just like python :
>>> " ".join( map(str, [1,2,3,4]) )
as @capone did ,
std::string join(const std::vector<std::string> &str_list ,
const std::string &delim=" ")
{
if(str_list.size() == 0) return "" ;
return std::accumulate( str_list.cbegin() + 1,
str_list.cend(),
str_list.at(0) ,
[&delim](const std::string &a , const std::string &b)
{
return a + delim + b ;
} ) ;
}
template <typename ST , typename TT>
std::vector<TT> map(TT (*op)(ST) , const vector<ST> &ori_vec)
{
vector<TT> rst ;
std::transform(ori_vec.cbegin() ,
ori_vec.cend() , back_inserter(rst) ,
[&op](const ST& val){ return op(val) ;} ) ;
return rst ;
}
Then we can call like following :
int main(int argc , char *argv)
{
vector<int> int_vec = {1,2,3,4} ;
vector<string> str_vec = map<int,string>(to_string, int_vec) ;
cout << join(str_vec) << endl ;
return 0 ;
}
just like python :
>>> " ".join( map(str, [1,2,3,4]) )
answered May 28 '16 at 14:55
小文件小文件
556
556
add a comment |
add a comment |
I use something like this
namespace std
{
// for strings join
string to_string( string value )
{
return value;
}
} // namespace std
namespace // anonymous
{
template< typename T >
std::string join( const std::vector<T>& values, char delimiter )
{
std::string result;
for( typename std::vector<T>::size_type idx = 0; idx < values.size(); ++idx )
{
if( idx != 0 )
result += delimiter;
result += std::to_string( values[idx] );
}
return result;
}
} // namespace anonymous
add a comment |
I use something like this
namespace std
{
// for strings join
string to_string( string value )
{
return value;
}
} // namespace std
namespace // anonymous
{
template< typename T >
std::string join( const std::vector<T>& values, char delimiter )
{
std::string result;
for( typename std::vector<T>::size_type idx = 0; idx < values.size(); ++idx )
{
if( idx != 0 )
result += delimiter;
result += std::to_string( values[idx] );
}
return result;
}
} // namespace anonymous
add a comment |
I use something like this
namespace std
{
// for strings join
string to_string( string value )
{
return value;
}
} // namespace std
namespace // anonymous
{
template< typename T >
std::string join( const std::vector<T>& values, char delimiter )
{
std::string result;
for( typename std::vector<T>::size_type idx = 0; idx < values.size(); ++idx )
{
if( idx != 0 )
result += delimiter;
result += std::to_string( values[idx] );
}
return result;
}
} // namespace anonymous
I use something like this
namespace std
{
// for strings join
string to_string( string value )
{
return value;
}
} // namespace std
namespace // anonymous
{
template< typename T >
std::string join( const std::vector<T>& values, char delimiter )
{
std::string result;
for( typename std::vector<T>::size_type idx = 0; idx < values.size(); ++idx )
{
if( idx != 0 )
result += delimiter;
result += std::to_string( values[idx] );
}
return result;
}
} // namespace anonymous
answered Jun 7 '16 at 6:41
shinshillovshinshillov
114
114
add a comment |
add a comment |
I started out with @sbi's answer but most of the time ended up piping the resulting string to a stream so created the below solution that can be piped to a stream without the overhead of creating the full string in memory.
It is used as follows:
#include "string_join.h"
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3, 4 };
// String version
std::string str = join(v, std::string(", "));
std::cout << str << std::endl;
// Directly piped to stream version
std::cout << join(v, std::string(", ")) << std::endl;
}
Where string_join.h is:
#pragma once
#include <iterator>
#include <sstream>
template<typename Str, typename It>
class joined_strings
{
private:
const It begin, end;
Str sep;
public:
typedef typename Str::value_type char_type;
typedef typename Str::traits_type traits_type;
typedef typename Str::allocator_type allocator_type;
private:
typedef std::basic_ostringstream<char_type, traits_type, allocator_type>
ostringstream_type;
public:
joined_strings(It begin, const It end, const Str &sep)
: begin(begin), end(end), sep(sep)
{
}
operator Str() const
{
ostringstream_type result;
result << *this;
return result.str();
}
template<typename ostream_type>
friend ostream_type& operator<<(
ostream_type &ostr, const joined_strings<Str, It> &joined)
{
It it = joined.begin;
if(it!=joined.end)
ostr << *it;
for(++it; it!=joined.end; ++it)
ostr << joined.sep << *it;
return ostr;
}
};
template<typename Str, typename It>
inline joined_strings<Str, It> join(It begin, const It end, const Str &sep)
{
return joined_strings<Str, It>(begin, end, sep);
}
template<typename Str, typename Container>
inline joined_strings<Str, typename Container::const_iterator> join(
Container container, const Str &sep)
{
return join(container.cbegin(), container.cend(), sep);
}
add a comment |
I started out with @sbi's answer but most of the time ended up piping the resulting string to a stream so created the below solution that can be piped to a stream without the overhead of creating the full string in memory.
It is used as follows:
#include "string_join.h"
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3, 4 };
// String version
std::string str = join(v, std::string(", "));
std::cout << str << std::endl;
// Directly piped to stream version
std::cout << join(v, std::string(", ")) << std::endl;
}
Where string_join.h is:
#pragma once
#include <iterator>
#include <sstream>
template<typename Str, typename It>
class joined_strings
{
private:
const It begin, end;
Str sep;
public:
typedef typename Str::value_type char_type;
typedef typename Str::traits_type traits_type;
typedef typename Str::allocator_type allocator_type;
private:
typedef std::basic_ostringstream<char_type, traits_type, allocator_type>
ostringstream_type;
public:
joined_strings(It begin, const It end, const Str &sep)
: begin(begin), end(end), sep(sep)
{
}
operator Str() const
{
ostringstream_type result;
result << *this;
return result.str();
}
template<typename ostream_type>
friend ostream_type& operator<<(
ostream_type &ostr, const joined_strings<Str, It> &joined)
{
It it = joined.begin;
if(it!=joined.end)
ostr << *it;
for(++it; it!=joined.end; ++it)
ostr << joined.sep << *it;
return ostr;
}
};
template<typename Str, typename It>
inline joined_strings<Str, It> join(It begin, const It end, const Str &sep)
{
return joined_strings<Str, It>(begin, end, sep);
}
template<typename Str, typename Container>
inline joined_strings<Str, typename Container::const_iterator> join(
Container container, const Str &sep)
{
return join(container.cbegin(), container.cend(), sep);
}
add a comment |
I started out with @sbi's answer but most of the time ended up piping the resulting string to a stream so created the below solution that can be piped to a stream without the overhead of creating the full string in memory.
It is used as follows:
#include "string_join.h"
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3, 4 };
// String version
std::string str = join(v, std::string(", "));
std::cout << str << std::endl;
// Directly piped to stream version
std::cout << join(v, std::string(", ")) << std::endl;
}
Where string_join.h is:
#pragma once
#include <iterator>
#include <sstream>
template<typename Str, typename It>
class joined_strings
{
private:
const It begin, end;
Str sep;
public:
typedef typename Str::value_type char_type;
typedef typename Str::traits_type traits_type;
typedef typename Str::allocator_type allocator_type;
private:
typedef std::basic_ostringstream<char_type, traits_type, allocator_type>
ostringstream_type;
public:
joined_strings(It begin, const It end, const Str &sep)
: begin(begin), end(end), sep(sep)
{
}
operator Str() const
{
ostringstream_type result;
result << *this;
return result.str();
}
template<typename ostream_type>
friend ostream_type& operator<<(
ostream_type &ostr, const joined_strings<Str, It> &joined)
{
It it = joined.begin;
if(it!=joined.end)
ostr << *it;
for(++it; it!=joined.end; ++it)
ostr << joined.sep << *it;
return ostr;
}
};
template<typename Str, typename It>
inline joined_strings<Str, It> join(It begin, const It end, const Str &sep)
{
return joined_strings<Str, It>(begin, end, sep);
}
template<typename Str, typename Container>
inline joined_strings<Str, typename Container::const_iterator> join(
Container container, const Str &sep)
{
return join(container.cbegin(), container.cend(), sep);
}
I started out with @sbi's answer but most of the time ended up piping the resulting string to a stream so created the below solution that can be piped to a stream without the overhead of creating the full string in memory.
It is used as follows:
#include "string_join.h"
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3, 4 };
// String version
std::string str = join(v, std::string(", "));
std::cout << str << std::endl;
// Directly piped to stream version
std::cout << join(v, std::string(", ")) << std::endl;
}
Where string_join.h is:
#pragma once
#include <iterator>
#include <sstream>
template<typename Str, typename It>
class joined_strings
{
private:
const It begin, end;
Str sep;
public:
typedef typename Str::value_type char_type;
typedef typename Str::traits_type traits_type;
typedef typename Str::allocator_type allocator_type;
private:
typedef std::basic_ostringstream<char_type, traits_type, allocator_type>
ostringstream_type;
public:
joined_strings(It begin, const It end, const Str &sep)
: begin(begin), end(end), sep(sep)
{
}
operator Str() const
{
ostringstream_type result;
result << *this;
return result.str();
}
template<typename ostream_type>
friend ostream_type& operator<<(
ostream_type &ostr, const joined_strings<Str, It> &joined)
{
It it = joined.begin;
if(it!=joined.end)
ostr << *it;
for(++it; it!=joined.end; ++it)
ostr << joined.sep << *it;
return ostr;
}
};
template<typename Str, typename It>
inline joined_strings<Str, It> join(It begin, const It end, const Str &sep)
{
return joined_strings<Str, It>(begin, end, sep);
}
template<typename Str, typename Container>
inline joined_strings<Str, typename Container::const_iterator> join(
Container container, const Str &sep)
{
return join(container.cbegin(), container.cend(), sep);
}
answered Jan 26 '17 at 11:42
Nathan PhillipsNathan Phillips
8,81412217
8,81412217
add a comment |
add a comment |
I have wrote the following code. It is based in C# string.join. It works with std::string and std::wstring and many type of vectors. (examples in comments)
Call it like this:
std::vector<int> vVectorOfIds = {1, 2, 3, 4, 5};
std::wstring wstrStringForSQLIn = Join(vVectorOfIds, L',');
Code:
// Generic Join template (mimics string.Join() from C#)
// Written by RandomGuy (stackoverflow) 09-01-2017
// Based on Brian R. Bondy anwser here:
// http://stackoverflow.com/questions/1430757/c-vector-to-string
// Works with char, wchar_t, std::string and std::wstring delimiters
// Also works with a different types of vectors like ints, floats, longs
template<typename T, typename D>
auto Join(const std::vector<T> &vToMerge, const D &delimiter)
{
// We use std::conditional to get the correct type for the stringstream (char or wchar_t)
// stringstream = basic_stringstream<char>, wstringstream = basic_stringstream<wchar_t>
using strType =
std::conditional<
std::is_same<D, std::string>::value,
char,
std::conditional<
std::is_same<D, char>::value,
char,
wchar_t
>::type
>::type;
std::basic_stringstream<strType> ss;
for (size_t i = 0; i < vToMerge.size(); ++i)
{
if (i != 0)
ss << delimiter;
ss << vToMerge[i];
}
return ss.str();
}
add a comment |
I have wrote the following code. It is based in C# string.join. It works with std::string and std::wstring and many type of vectors. (examples in comments)
Call it like this:
std::vector<int> vVectorOfIds = {1, 2, 3, 4, 5};
std::wstring wstrStringForSQLIn = Join(vVectorOfIds, L',');
Code:
// Generic Join template (mimics string.Join() from C#)
// Written by RandomGuy (stackoverflow) 09-01-2017
// Based on Brian R. Bondy anwser here:
// http://stackoverflow.com/questions/1430757/c-vector-to-string
// Works with char, wchar_t, std::string and std::wstring delimiters
// Also works with a different types of vectors like ints, floats, longs
template<typename T, typename D>
auto Join(const std::vector<T> &vToMerge, const D &delimiter)
{
// We use std::conditional to get the correct type for the stringstream (char or wchar_t)
// stringstream = basic_stringstream<char>, wstringstream = basic_stringstream<wchar_t>
using strType =
std::conditional<
std::is_same<D, std::string>::value,
char,
std::conditional<
std::is_same<D, char>::value,
char,
wchar_t
>::type
>::type;
std::basic_stringstream<strType> ss;
for (size_t i = 0; i < vToMerge.size(); ++i)
{
if (i != 0)
ss << delimiter;
ss << vToMerge[i];
}
return ss.str();
}
add a comment |
I have wrote the following code. It is based in C# string.join. It works with std::string and std::wstring and many type of vectors. (examples in comments)
Call it like this:
std::vector<int> vVectorOfIds = {1, 2, 3, 4, 5};
std::wstring wstrStringForSQLIn = Join(vVectorOfIds, L',');
Code:
// Generic Join template (mimics string.Join() from C#)
// Written by RandomGuy (stackoverflow) 09-01-2017
// Based on Brian R. Bondy anwser here:
// http://stackoverflow.com/questions/1430757/c-vector-to-string
// Works with char, wchar_t, std::string and std::wstring delimiters
// Also works with a different types of vectors like ints, floats, longs
template<typename T, typename D>
auto Join(const std::vector<T> &vToMerge, const D &delimiter)
{
// We use std::conditional to get the correct type for the stringstream (char or wchar_t)
// stringstream = basic_stringstream<char>, wstringstream = basic_stringstream<wchar_t>
using strType =
std::conditional<
std::is_same<D, std::string>::value,
char,
std::conditional<
std::is_same<D, char>::value,
char,
wchar_t
>::type
>::type;
std::basic_stringstream<strType> ss;
for (size_t i = 0; i < vToMerge.size(); ++i)
{
if (i != 0)
ss << delimiter;
ss << vToMerge[i];
}
return ss.str();
}
I have wrote the following code. It is based in C# string.join. It works with std::string and std::wstring and many type of vectors. (examples in comments)
Call it like this:
std::vector<int> vVectorOfIds = {1, 2, 3, 4, 5};
std::wstring wstrStringForSQLIn = Join(vVectorOfIds, L',');
Code:
// Generic Join template (mimics string.Join() from C#)
// Written by RandomGuy (stackoverflow) 09-01-2017
// Based on Brian R. Bondy anwser here:
// http://stackoverflow.com/questions/1430757/c-vector-to-string
// Works with char, wchar_t, std::string and std::wstring delimiters
// Also works with a different types of vectors like ints, floats, longs
template<typename T, typename D>
auto Join(const std::vector<T> &vToMerge, const D &delimiter)
{
// We use std::conditional to get the correct type for the stringstream (char or wchar_t)
// stringstream = basic_stringstream<char>, wstringstream = basic_stringstream<wchar_t>
using strType =
std::conditional<
std::is_same<D, std::string>::value,
char,
std::conditional<
std::is_same<D, char>::value,
char,
wchar_t
>::type
>::type;
std::basic_stringstream<strType> ss;
for (size_t i = 0; i < vToMerge.size(); ++i)
{
if (i != 0)
ss << delimiter;
ss << vToMerge[i];
}
return ss.str();
}
edited Aug 10 '17 at 13:14
answered Aug 10 '17 at 11:46
RandomGuyRandomGuy
346415
346415
add a comment |
add a comment |
Expanding on the attempt of @sbi at a generic solution that is not restricted to std::vector<int>
or a specific return string type. The code presented below can be used like this:
std::vector<int> vec{ 1, 2, 3 };
// Call modern range-based overload.
auto str = join( vec, "," );
auto wideStr = join( vec, L"," );
// Call old-school iterator-based overload.
auto str = join( vec.begin(), vec.end(), "," );
auto wideStr = join( vec.begin(), vec.end(), L"," );
In the original code, template argument deduction does not work to produce the right return string type if the separator is a string literal (as in the samples above). In this case, the typedefs like Str::value_type
in the function body are incorrect. The code assumes that Str
is always a type like std::basic_string
, so it obviously fails for string literals.
To fix this, the following code tries to deduce only the character type from the separator argument and uses that to produce a default return string type. This is achieved using boost::range_value
, which extracts the element type from the given range type.
#include <string>
#include <sstream>
#include <boost/range.hpp>
template< class Sep, class Str = std::basic_string< typename boost::range_value< Sep >::type >, class InputIt >
Str join( InputIt first, const InputIt last, const Sep& sep )
{
using char_type = typename Str::value_type;
using traits_type = typename Str::traits_type;
using allocator_type = typename Str::allocator_type;
using ostringstream_type = std::basic_ostringstream< char_type, traits_type, allocator_type >;
ostringstream_type result;
if( first != last )
{
result << *first++;
}
while( first != last )
{
result << sep << *first++;
}
return result.str();
}
Now we can easily provide a range-based overload that simply forwards to the iterator-based overload:
template <class Sep, class Str = std::basic_string< typename boost::range_value<Sep>::type >, class InputRange>
Str join( const InputRange &input, const Sep &sep )
{
// Include the standard begin() and end() in the overload set for ADL. This makes the
// function work for standard types (including arrays), aswell as any custom types
// that have begin() and end() member functions or overloads of the standalone functions.
using std::begin; using std::end;
// Call iterator-based overload.
return join( begin(input), end(input), sep );
}
Live Demo at Coliru
add a comment |
Expanding on the attempt of @sbi at a generic solution that is not restricted to std::vector<int>
or a specific return string type. The code presented below can be used like this:
std::vector<int> vec{ 1, 2, 3 };
// Call modern range-based overload.
auto str = join( vec, "," );
auto wideStr = join( vec, L"," );
// Call old-school iterator-based overload.
auto str = join( vec.begin(), vec.end(), "," );
auto wideStr = join( vec.begin(), vec.end(), L"," );
In the original code, template argument deduction does not work to produce the right return string type if the separator is a string literal (as in the samples above). In this case, the typedefs like Str::value_type
in the function body are incorrect. The code assumes that Str
is always a type like std::basic_string
, so it obviously fails for string literals.
To fix this, the following code tries to deduce only the character type from the separator argument and uses that to produce a default return string type. This is achieved using boost::range_value
, which extracts the element type from the given range type.
#include <string>
#include <sstream>
#include <boost/range.hpp>
template< class Sep, class Str = std::basic_string< typename boost::range_value< Sep >::type >, class InputIt >
Str join( InputIt first, const InputIt last, const Sep& sep )
{
using char_type = typename Str::value_type;
using traits_type = typename Str::traits_type;
using allocator_type = typename Str::allocator_type;
using ostringstream_type = std::basic_ostringstream< char_type, traits_type, allocator_type >;
ostringstream_type result;
if( first != last )
{
result << *first++;
}
while( first != last )
{
result << sep << *first++;
}
return result.str();
}
Now we can easily provide a range-based overload that simply forwards to the iterator-based overload:
template <class Sep, class Str = std::basic_string< typename boost::range_value<Sep>::type >, class InputRange>
Str join( const InputRange &input, const Sep &sep )
{
// Include the standard begin() and end() in the overload set for ADL. This makes the
// function work for standard types (including arrays), aswell as any custom types
// that have begin() and end() member functions or overloads of the standalone functions.
using std::begin; using std::end;
// Call iterator-based overload.
return join( begin(input), end(input), sep );
}
Live Demo at Coliru
add a comment |
Expanding on the attempt of @sbi at a generic solution that is not restricted to std::vector<int>
or a specific return string type. The code presented below can be used like this:
std::vector<int> vec{ 1, 2, 3 };
// Call modern range-based overload.
auto str = join( vec, "," );
auto wideStr = join( vec, L"," );
// Call old-school iterator-based overload.
auto str = join( vec.begin(), vec.end(), "," );
auto wideStr = join( vec.begin(), vec.end(), L"," );
In the original code, template argument deduction does not work to produce the right return string type if the separator is a string literal (as in the samples above). In this case, the typedefs like Str::value_type
in the function body are incorrect. The code assumes that Str
is always a type like std::basic_string
, so it obviously fails for string literals.
To fix this, the following code tries to deduce only the character type from the separator argument and uses that to produce a default return string type. This is achieved using boost::range_value
, which extracts the element type from the given range type.
#include <string>
#include <sstream>
#include <boost/range.hpp>
template< class Sep, class Str = std::basic_string< typename boost::range_value< Sep >::type >, class InputIt >
Str join( InputIt first, const InputIt last, const Sep& sep )
{
using char_type = typename Str::value_type;
using traits_type = typename Str::traits_type;
using allocator_type = typename Str::allocator_type;
using ostringstream_type = std::basic_ostringstream< char_type, traits_type, allocator_type >;
ostringstream_type result;
if( first != last )
{
result << *first++;
}
while( first != last )
{
result << sep << *first++;
}
return result.str();
}
Now we can easily provide a range-based overload that simply forwards to the iterator-based overload:
template <class Sep, class Str = std::basic_string< typename boost::range_value<Sep>::type >, class InputRange>
Str join( const InputRange &input, const Sep &sep )
{
// Include the standard begin() and end() in the overload set for ADL. This makes the
// function work for standard types (including arrays), aswell as any custom types
// that have begin() and end() member functions or overloads of the standalone functions.
using std::begin; using std::end;
// Call iterator-based overload.
return join( begin(input), end(input), sep );
}
Live Demo at Coliru
Expanding on the attempt of @sbi at a generic solution that is not restricted to std::vector<int>
or a specific return string type. The code presented below can be used like this:
std::vector<int> vec{ 1, 2, 3 };
// Call modern range-based overload.
auto str = join( vec, "," );
auto wideStr = join( vec, L"," );
// Call old-school iterator-based overload.
auto str = join( vec.begin(), vec.end(), "," );
auto wideStr = join( vec.begin(), vec.end(), L"," );
In the original code, template argument deduction does not work to produce the right return string type if the separator is a string literal (as in the samples above). In this case, the typedefs like Str::value_type
in the function body are incorrect. The code assumes that Str
is always a type like std::basic_string
, so it obviously fails for string literals.
To fix this, the following code tries to deduce only the character type from the separator argument and uses that to produce a default return string type. This is achieved using boost::range_value
, which extracts the element type from the given range type.
#include <string>
#include <sstream>
#include <boost/range.hpp>
template< class Sep, class Str = std::basic_string< typename boost::range_value< Sep >::type >, class InputIt >
Str join( InputIt first, const InputIt last, const Sep& sep )
{
using char_type = typename Str::value_type;
using traits_type = typename Str::traits_type;
using allocator_type = typename Str::allocator_type;
using ostringstream_type = std::basic_ostringstream< char_type, traits_type, allocator_type >;
ostringstream_type result;
if( first != last )
{
result << *first++;
}
while( first != last )
{
result << sep << *first++;
}
return result.str();
}
Now we can easily provide a range-based overload that simply forwards to the iterator-based overload:
template <class Sep, class Str = std::basic_string< typename boost::range_value<Sep>::type >, class InputRange>
Str join( const InputRange &input, const Sep &sep )
{
// Include the standard begin() and end() in the overload set for ADL. This makes the
// function work for standard types (including arrays), aswell as any custom types
// that have begin() and end() member functions or overloads of the standalone functions.
using std::begin; using std::end;
// Call iterator-based overload.
return join( begin(input), end(input), sep );
}
Live Demo at Coliru
answered Sep 26 '18 at 19:49
zett42zett42
7,6733841
7,6733841
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f1430757%2fconvert-a-vectorint-to-a-string%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Closely related: stackoverflow.com/questions/4850473/…
– Ciro Santilli 新疆改造中心996ICU六四事件
Mar 8 '15 at 21:39