Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can boost:algorithm::join() concat a container of floats?

Boost join can be used to concatenate a container of strings optionally separated by a separator string as shown in this example: A good example for boost::algorithm::join

My STL skills are weak. I'm wondering if there is any way to use the same function for a container of numbers (floats, doubles, ints)? It just seems like there should some one or two-liner to adapt it for other types.

There is also stl's copy function with a good example found here: How to print out the contents of a vector?

But I don't like how it adds the separator string after every element. I'd like to just use boost.

like image 874
Bitdiot Avatar asked Jun 06 '13 14:06

Bitdiot


2 Answers

Sure, you can combine boost::algorithm::join and boost::adaptors::transformed to convert the doubles to strings and then join them together.

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

#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>

int main()
{
    using boost::adaptors::transformed;
    using boost::algorithm::join;

    std::vector<double> v{1.1, 2.2, 3.3, 4.4};

    std::cout 
      << join( v | 
               transformed( static_cast<std::string(*)(double)>(std::to_string) ), 
               ", " );
}

Output:

1.100000, 2.200000, 3.300000, 4.400000


You can also use a lambda to avoid the ugly cast

join(v | transformed([](double d) { return std::to_string(d); }), ", ")
like image 130
Praetorian Avatar answered Sep 18 '22 09:09

Praetorian


My STL skills are weak. I'm wondering if there is anyway to use the same function for a container of numbers (floats, doubles, ints)? It just seems like there should some one- or two-liner to adapt it for other types.

std::accumulate allows to do a fold over any (input) iterator range, using a binary function which can take different types for the "accumulator" and the next item. In your case: A function taking a std::string and a double (or whatever) that concatenates the given std::string with the result of std::to_string on the second parameter.

template<typename Container>
std::string contents_as_string(Container const & c,
                               std::string const & separator) {
  if (c.size() == 0) return "";
  auto fold_operation = [&separator] (std::string const & accum,
                                      auto const & item) {
    return accum + separator + std::to_string(item);};
  return std::accumulate(std::next(std::begin(c)), std::end(c),
                         std::to_string(*std::begin(c)), fold_operation);
}

As you can see, this is completely independent of the value type of the container. As long as you can pass it to std::to_string you're good. Actually, above code is a slight variation of the example presented for std::accumulate.

Demo of above function:

int main() {
  std::vector<double> v(4);
  std::iota(std::begin(v), std::end(v), 0.1);
  std::cout << contents_as_string(v, ", ") << std::endl;

  std::vector<int> w(5);
  std::iota(std::begin(w), std::end(w), 1);
  std::cout << contents_as_string(w, " x ") << " = "
    << std::accumulate(std::begin(w), std::end(w), 1, std::multiplies<int>{})
    << std::endl;
}

0.100000, 1.100000, 2.100000, 3.100000
1 x 2 x 3 x 4 x 5 = 120

like image 39
Daniel Jour Avatar answered Sep 18 '22 09:09

Daniel Jour