4

Suppose a vector with values [0,1,2,3,4,5,6,7,8,9]. How can I create a vector that refers to not necessarily contiguous values, e.g. [3,4,7,9], i.e. given by some index, by using STL.

  • Uncannily similar to http://stackoverflow.com/questions/9705441 but non-sequential. – Flexo Mar 14 '12 at 16:36
  • can you apply the same solution? The question just popped from that. would you still say that if I would ask in 2 days time? –  Mar 14 '12 at 16:37
  • Similar, but also different enough to require a different solution. – juanchopanza Mar 14 '12 at 16:39
  • 1
    @g24l so you want an imaginary/equivalent function like/to `std::vector::insert_offsets (original_vector, {3, 4, 7, 9})`? Where the second parameter is the offsets you wan't your new vector to contain. there is nothing regarding the values that is to be taken into account? – Filip Roséen - refp Mar 14 '12 at 16:43
  • I fail to understand the downvote though... dah ... –  Mar 14 '12 at 16:54

1 Answers1

4

You can express this as a transformation, e.g.:

#include <valarray>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>

template <typename T>
void pick(std::vector<T>& result, const std::vector<T>& in, const std::vector<typename std::vector<T>::size_type>& s) {
  result.reserve(s.size());
  std::transform(s.begin(), s.end(), std::back_inserter(result),
                 [&in](typename std::vector<T>::size_type idx) {
                   return in.at(idx);
                 });
}

int main() {
  const std::vector<int> arr={0,1,2,3,4,5,6,7,8,9,10};
  std::vector<int> result;
  pick(result, arr, {3,4,7,9});
}

I used a lambda, but you can also use std::bind or the (now deprecated) std::bind2nd for this.

The example with C++11's std::bind makes pick:

template <typename T>
void pick(std::vector<T>& result, const std::vector<T>& in, const std::vector<typename std::vector<T>::size_type>& s) {
  result.reserve(s.size());
  std::transform(s.begin(), s.end(), std::back_inserter(result),
                 std::bind(static_cast<const T& (std::vector<T>::*)(typename std::vector<T>::size_type) const>(&std::vector<T>::at),  in, std::placeholders::_1));
}

It's seriously ugly though because of the need to cast the member function pointer to resolve the overload of at (const vs non-const versions).

Community
  • 1
  • 1
Flexo
  • 87,323
  • 22
  • 191
  • 272
  • 1
    yes , I was thinking `std::bind2nd` also, but `std::transform` is very nice. Beautiful solution! –  Mar 14 '12 at 16:46
  • @refp - it gets worse with the `std::bind` version too ;) – Flexo Mar 14 '12 at 16:55
  • @g24l I'm currently in a very boring meeting (where I shouldn't be), if interested I could write you a much nicer solution in a bit. – Filip Roséen - refp Mar 14 '12 at 16:55
  • @refp same here, nothing better to do myself...so I'm interested! –  Mar 14 '12 at 16:57
  • @g24l just to make things clear; the offsets are **not** known at compile-time, right? – Filip Roséen - refp Mar 14 '12 at 16:57
  • @refp - I'd be curious to see that. I don't see how it's possible to write a cleaner solution without writing a for loop out by hand which I assume is what the OP meant when they said *"using STL"*. For the record in real code I've either used a type that support arbitrary slicing (not just taking a slice with a constant stride) or written it out the long way. – Flexo Mar 14 '12 at 16:58
  • @refp : I would suppose not. I don't see much utility to it, although there are cases that you would want to. –  Mar 17 '12 at 08:24