1

Is there a standard algorithm that I can use to subset a vector of structs by a vector of pointers to these structs?

E.g.

typedef struct {
    int a;
    char b;
} some_struct_t;

int main()
{
    std::vector<some_struct_t> v = { {10, '1'}, { 5, '2'} };
    std::vector<some_struct_t*> v2;

    std::some_subset_command<some_struct_t, some_struct_t*>(begin(v), end(v), std::back_inserter(v2), [](const somestruct&) { if (somestruct.a == 5) { return &somestruct; }});
}

some_subset_command() should construct the new vector using the back_inserter and the output from the lambda predicate.

How can I do this in the most declarative way?

cigien
  • 57,834
  • 11
  • 73
  • 112
glades
  • 3,778
  • 1
  • 12
  • 34
  • Relevant: [Why is there no transform_if in the C++ standard library?](https://stackoverflow.com/q/23579832/580083) and [copy_if with different types](https://stackoverflow.com/q/50914615/580083). – Daniel Langr Oct 12 '21 at 06:22

2 Answers2

4

If you don't mind changing your std::vector<some_struct_t*> v2 to a std::vector<std::reference_wrapper<some_struct_t>> v2 (include <functional>), you could use std::copy_if:

#include <vector>
#include <algorithm>
#include <functional>

typedef struct {
    int a;
    char b;
} some_struct_t;

int main()
{
    std::vector<some_struct_t> v = { {10, '1'}, { 5, '2'} };
    std::vector<std::reference_wrapper<some_struct_t>> v2;
    std::copy_if(begin(v), end(v), std::back_inserter(v2), [](const auto &s) {
        return s.a == 5;
    });
}

More information for std::reference_wrapper at cppreference.

mediocrevegetable1
  • 4,086
  • 1
  • 11
  • 33
  • Thank you that's very cool. I didn't know about reference wrappers! Can I then use the member access operator (.) on a reference wrapper object? – glades Oct 12 '21 at 06:47
  • @glades since it's a wrapper you'll have to use `get()` to get the referenced object. (e.g. `v2[0].get().a = 1;`. I'll edit my post to add a link to some docs. – mediocrevegetable1 Oct 12 '21 at 06:52
2

If your compiler supports C++20, then you can use range adaptors:

#include <ranges>
#include <vector>

typedef struct {
    int a;
    char b;
} some_struct_t;

int main()
{
  std::vector<some_struct_t> v = { {10, '1'}, { 5, '2'} };
  auto r = v | std::views::filter([](auto& somestruct) { return somestruct.a == 5; })
             | std::views::transform([](auto& somestruct) { return &somestruct; });       
  std::vector<some_struct_t*> v2(r.begin(), r.end());
}

Demo.

康桓瑋
  • 33,481
  • 5
  • 40
  • 90