3
struct Node
{
    std::string name;
    ...
};

typedef std::vector<Node> Nodes;

Nodes nodes;
std::vector<std::string> names;

Is there a nice one-liner way of populating the vector names with Node::name for each item in nodes?

This is what I currently do:

names.reserve(nodes.size());
BOOST_FOREACH(Node node, nodes) 
{
    names.push_back(node.name);
}

I'm using C++98, std and boost.

Baz
  • 12,713
  • 38
  • 145
  • 268

4 Answers4

6

This is much simpler with newer libraries (boost) and or standards (C++11), but you should be able to write a small functor:

struct ReadNodeName {
   std::string const & operator()(Node const & node) const {
      return node.name;
   }
};

And then use std::transform

std::transform(nodes.begin(), nodes.end(),
               std::back_inserter(names),
               ReadNodeName());

With boost, the functor could be just boost::bind, in C++11 you could use a lambda: [](Node const & node) { return node.name; };, in C++14 a shorter lambda [](auto& n) { return n.name; }:

std::transform(nodes.begin(), nodes.end(),
               std::back_inserter(names),
               [](auto & n){return n.name;});

Using boost bind -- beware, untested, I don't have access to boost

std::transform(nodes.begin(), nodes.end(),
               std::back_inserter(names),
               boost::bind(&Node::name, _1));
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
4

Use std::transform. This takes care of the actual copying in one line. You will have to write a transformation function, though.

Oswald
  • 31,254
  • 3
  • 43
  • 68
4

If you add a getName() function to Node, you can do this:

std::transform(nodes.begin(), nodes.end(), back_inserter(names), mem_fun(&Node::getName));
Tobias Brandt
  • 3,393
  • 18
  • 28
  • I'd like Node to feel more like a data container than a class. It starts feeling more and more like a class if I add more and more functions to it. This is why I define it using struct instead of class. – Baz Oct 24 '13 at 12:50
  • @Baz while I understand where you're coming from, adding member functions does not change anything about your object under the hood. It's still plain old data until you start adding in virtual functions etc. – bstamour Oct 24 '13 at 12:59
  • You could just write a free function that takes a `Node` and returns its `string`, and pass the name of that function as the last argument to transform. – Aaron McDaid Oct 24 '13 at 15:31
2

Since you mention boost, here is one liner with boost::lambda:

std::transform(nodes.begin(),nodes.end(), std::back_inserter(names), &boost::lambda::_1 ->* &Node::name );

Boost lambda cannot overload .* so first we need to get address &, and then use ->* to get reference to member.

Arpegius
  • 5,817
  • 38
  • 53