Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Print comma separated list from std::vector [duplicate]

I'm trying to print a comma separated list of a single detail from a std::vector<MyClass>. So far the simplest and cleverest way I have seen to do this is to use

std::ostringstream ss;
std::copy(vec.begin(), vec.end() - 1, std::ostream_iterator<std::string>(ss, ", "))
ss << vec.back();

That worked fine when I was printing a vector of strings. However, now I am trying to print a single detail about MyClass. I know in Python I could do something like

(x.specific_detail for x in vec)

to get a generator expression for the thing that I am interested in. I'm wondering if I can do something similar here or if I am stuck doing

for (auto it = vec.begin(); it != vec.end(); ++it) {
    // Do stuff here
}
like image 929
Nick Chapman Avatar asked Sep 18 '17 14:09

Nick Chapman


2 Answers

One way of solving this I have seen is:

std::string separator;
for (auto x : vec) {
  ss << separator << x.specific_detail;
  separator = ",";
}
like image 151
Chris Drew Avatar answered Nov 19 '22 08:11

Chris Drew


A fairly easy and reusable way:

#include <vector>
#include <iostream>

template<class Stream, class T, class A>
Stream& printem(Stream&os, std::vector<T, A> const& v)
{
    auto emit = [&os, need_comma = false](T const& x) mutable
    {
        if (need_comma) os << ", ";
        os << x;
        need_comma = true;
    };

    for(T const& x : v) emit(x);
    return os;
}


int main()
{
    auto v = std::vector<int> { 1, 2, 3, 4 , 5 };

    printem(std::cout, v) << std::endl;
}

And another way which defines an extendable protocol for printing containers:

#include <vector>
#include <iostream>

template<class Container>
struct container_printer;

// specialise for a class of container
template<class T, class A>
struct container_printer<std::vector<T, A>>
{
    using container_type = std::vector<T, A>;

    container_printer(container_type const& c) : c(c) {}

    std::ostream& operator()(std::ostream& os) const 
    {
        const char* sep = "";
        for (const T& x : c) {
            os << sep << x;
            sep = ", ";
        }
        return os;
    }

    friend std::ostream& operator<<(std::ostream& os, container_printer const& cp)
    {
        return cp(os);
    }

    container_type c;
};

template<class Container>
auto print_container(Container&& c)
{
    using container_type = typename std::decay<Container>::type;
    return container_printer<container_type>(c);
}


int main()
{
    auto v = std::vector<int> { 1, 2, 3, 4 , 5 };

    std::cout << print_container(v) << std::endl;
}

...of course we can go further...

#include <vector>
#include <iostream>

template<class...Stuff>
struct container_printer;

// specialise for a class of container
template<class T, class A, class Separator, class Gap, class Prefix, class Postfix>
struct container_printer<std::vector<T, A>, Separator, Gap, Prefix, Postfix>
{
    using container_type = std::vector<T, A>;

    container_printer(container_type const& c, Separator sep, Gap gap, Prefix prefix, Postfix postfix) 
    : c(c)
    , separator(sep)
    , gap(gap)
    , prefix(prefix)
    , postfix(postfix) {}

    std::ostream& operator()(std::ostream& os) const 
    {
        Separator sep = gap;
        os << prefix;
        for (const T& x : c) {
            os << sep << x;
            sep = separator;
        }
        return os << gap << postfix; 
    }

    friend std::ostream& operator<<(std::ostream& os, container_printer const& cp)
    {
        return cp(os);
    }

    container_type c;
    Separator separator;
    Gap gap;
    Prefix prefix;
    Postfix postfix;
};

template<class Container, class Sep = char, class Gap = Sep, class Prefix = char, class Postfix = char>
auto print_container(Container&& c, Sep sep = ',', Gap gap = ' ', Prefix prefix = '[', Postfix postfix = ']')
{
    using container_type = typename std::decay<Container>::type;
    return container_printer<container_type, Sep, Gap, Prefix, Postfix>(c, sep, gap, prefix, postfix);
}


int main()
{
    auto v = std::vector<int> { 1, 2, 3, 4 , 5 };

    // json-style
    std::cout << print_container(v) << std::endl;

    // custom
    std::cout << print_container(v, " : ", " ", "(", ")") << std::endl;

    // custom
    std::cout << print_container(v, "-", "", ">>>", "<<<") << std::endl;

}

expected output:

[ 1,2,3,4,5 ]
( 1 : 2 : 3 : 4 : 5 )
>>>1-2-3-4-5<<<
like image 3
Richard Hodges Avatar answered Nov 19 '22 07:11

Richard Hodges