3

I'd like to create std::string that contains first elements of std::map<std::string, std::string> separate by some delimiter (it can be from STL or Boost). Is there any better solution (one line) than loop? Like boost::algorithm::join for std::vector<std::string>.

peter55555
  • 1,413
  • 1
  • 19
  • 36
  • 1
    It is unclear. Say you have a map with two elements. The output you are looking for is something like "key1 : value1, key2 : value2"? – Rerito Sep 18 '16 at 08:51
  • see [How to retrieve all keys (or values) from a std::map?](http://stackoverflow.com/questions/110157/how-to-retrieve-all-keys-or-values-from-a-stdmap), modify the solutions to append to string – M.M Sep 18 '16 at 08:54
  • No, @Rerito . For: "key1 : value1, key2 : value2" and comma delimiter I'd like to have: "key1,key2". – peter55555 Sep 18 '16 at 09:50

5 Answers5

7

This can be done elegantly using Boost.Range's map_keys and Boost.StringAlgo's join:

std::string get_keys(const std::map<std::string, std::string>& map) {
  return boost::algorithm::join(
    map | boost::adaptors::map_keys,
    ", ");
}

http://www.boost.org/doc/libs/1_61_0/boost/algorithm/string/join.hpp

http://www.boost.org/doc/libs/1_61_0/libs/range/doc/html/range/reference/adaptors/reference/map_keys.html

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
5

If you don't want to use boost, then try std::accumulate:

const std::string delimiter = "#";
const std::string result = std::accumulate(M.begin(), M.end(), std::string(),
[delimiter](const std::string& s, const std::pair<const std::string, std::string>& p) {
    return s + (s.empty() ? std::string() : delimiter) + p.first;
});

In the code above M is a std::map<std::string, std::string>.

Edgar Rokjān
  • 17,245
  • 4
  • 40
  • 67
1

Your algorithm should be something like this:

std::string get_keys(const std::map<std::string, std::string>& map) {
  std::string result;
  std::for_each(map.cbegin(),
                map.cend(),
                [&result](const decltype(map)::value_type& p) {
                  result += p.first;
                  result += ", ";
                });

  // Erase the last ", " from the string, if present
  if (result.size() > 0) {
    result.erase(result.size() - 2, 2);
  }

  return result;
}

Essentially you have to cycle for each element in the map and add it into the string. The complexity is O(N) with N the number of element in the map.

You can improve the performance of the algorithm applying reserve on the string result.

If you know the average length of the string key, the you can initialize the variable with:

std::string result;
result.reserve(map.size() * AVG_LENGTH_STR_KEY);

This will improve a lot the operator+= operation in the cycle.

BiagioF
  • 9,368
  • 2
  • 26
  • 50
1

As M.M. correctly pointed out, you can use boost::range for this (to which I added boost::string).

If your map is m, then the last line of

std::vector<std::string> o;
boost::copy(m | boost::adaptors::map_keys, std::back_inserter(o));
boost::algorithm::join(o, ", ");

is results. (Unfortunately, this requires a huge number of header files.)

Example

#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <boost/assign.hpp>
#include <boost/algorithm/string/join.hpp>
#include <algorithm>
#include <iostream>
#include <map>
#include <vector>

int main()
{
    std::map<std::string, std::string> m;
    m["hello"] = "world";
    m["goodbye"] = "now";

    std::vector<std::string> o;
    boost::copy(m | boost::adaptors::map_keys, std::back_inserter(o));
    std::cout << boost::algorithm::join(o, ", ") << std::endl;
}

This outputs

$ ./a.out 
goodbye, hello
$
Ami Tavory
  • 74,578
  • 11
  • 141
  • 185
0

if you are using a json library such as the header only nlohmann::json

you can simply try casting it to a json in a single line. note: be careful of exceptions.

std::cout << "My map or vector: " << ((json)myMap)) << "\n";

Edit: this might lazy i suppose but it gets the job done lol.

Mezo
  • 9
  • 4