35

I have a std::vector. I want to create iterators representing a slice of that vector. How do I do it? In pseudo C++:

class InterestingType;

void doSomething(slice& s) {
    for (slice::iterator i = s.begin(); i != s.end(); ++i) {
       std::cout << *i << endl;
    }
}
int main() {
   std::vector v();
   for (int i= 0; i < 10; ++i) { v.push_back(i); }
   slice slice1 = slice(v, 1, 5);
   slice slice2 = slice(v, 2, 4);
   doSomething(slice1);
   doSomething(slice2);
   return 0;
}

I would prefer not to have to copy the elements to a new datastructure.

Marco A.
  • 43,032
  • 26
  • 132
  • 246
  • 14
    Why is this community wiki? – Tom Jul 10 '09 at 16:21
  • 1
    Typically it is called a "slice" when you require both a range, and a stride not equal to one. For example see std::gslice and std::valarray. With a stride of one it would usually be called a "range". – Greg Rogers Jul 10 '09 at 17:24

8 Answers8

32

You'd just use a pair of iterators:

typedef std::vector<int>::iterator vec_iter;

void doSomething(vec_iter first, vec_iter last) {
    for (vec_iter cur = first; cur != last; ++cur) {
       std::cout << *cur << endl;
    }
}

int main() {
   std::vector v();
   for (int i= 0; i < 10; ++i) { v.push_back(i); }

   doSomething(v.begin() + 1, v.begin() + 5);
   doSomething(v.begin() + 2, v.begin() + 4);
   return 0;
}

Alternatively, the Boost.Range library should allow you to represent iterator pairs as a single object, but the above is the canonical way to do it.

jalf
  • 243,077
  • 51
  • 345
  • 550
9

I learnt Python before I learnt C++. I wondered if C++ offered slicing of vectors like slicing in Python lists. Took a couple of minutes to write this function that allows you to slice a vector analogous to the way its done in Python.

vector<int> slice(const vector<int>& v, int start=0, int end=-1) {
    int oldlen = v.size();
    int newlen;

    if (end == -1 or end >= oldlen){
        newlen = oldlen-start;
    } else {
        newlen = end-start;
    }

    vector<int> nv(newlen);

    for (int i=0; i<newlen; i++) {
        nv[i] = v[start+i];
    }
    return nv;
}

Usage:

vector<int> newvector = slice(vector_variable, start_index, end_index);

The start_index element will be included in the slice, whereas the end_index will not be included.

Example:

For a vector v1 like {1,3,5,7,9}

slice(v1,2,4) returns {5,7}

Pranjal Mittal
  • 10,772
  • 18
  • 74
  • 99
  • Python's slicing also has an optional third parameter, just like range(), for the step. This is why x[::-1] is a quick and easy way to reverse a string or list. – Kevin Jan 24 '14 at 23:23
  • 5
    The beauty about slices in most languages is that you can get a sub-vector/string without a copy operation. Your example copies the data element-by-element. But if you don't care about allocs or copy speed, fine. – dhardy Sep 26 '16 at 13:26
  • Don't use this function. There is no reason to have a special function just for vectors; at least generalize it to any container. And like others suggest - pass a range (Yes, I know C++ doesn't have ranges yet. But it will soon enough and this comment will still be here.) – einpoklum Apr 21 '17 at 20:50
  • In c++, you'll need to create a structure that holds the iterator start and end for the slice. – Erik Aronesty Aug 10 '18 at 15:35
5

Taken from here:

std::vector<myvector::value_type>(myvector.begin()+start, myvector.begin()+end).swap(myvector);

Usage example:

#include <iostream>
#include <vector>

int main ()
{
    std::vector<int> indexes{3, 6, 9};

    for( auto index : indexes )
    {
        int slice = 3;
        std::vector<int> bar{1, 2, 3, 4, 5, 6, 7, 8, 9};
        std::vector<int>( bar.begin() + index - slice, bar.begin() + index ).swap(bar);

        std::cout << "bar index " << index << " contains:";
        for (unsigned i=0; i<bar.size(); i++)
            std::cout << ' ' << bar[i];
        std::cout << '\n';
    }

    return 0;
}

Outputs:

bar index 3 contains: 1 2 3
bar index 6 contains: 4 5 6
bar index 9 contains: 7 8 9
Evandro Coan
  • 8,560
  • 11
  • 83
  • 144
jdehesa
  • 58,456
  • 7
  • 77
  • 121
2

You can represent those "slices" with a pair of iterators.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
Artur Soler
  • 2,974
  • 2
  • 23
  • 24
2

As others have said, you can represent the "slice" as pair of iterators. If you are willing to use Boost, you can use the range concept. Then you will have even begin()/end() member functions available and the whole thing looks a lot like a container.

wilx
  • 17,697
  • 6
  • 59
  • 114
2

use boost range adapters. they are lazy:

operator|() is used to add new behaviour lazily and never modifies its left argument.

boost::for_each(v|sliced(1,5)|transformed(doSomething));

doSomething needs to take range as input. a simple (may be lambda) wrapper would fix that.

kirill_igum
  • 3,953
  • 5
  • 47
  • 73
0

You don't need a pair of iterators to slice a vector. Three indexes will do because it allows you to create slices with steps:

static const int arr[] = {16,2,77,29,42};
vector<int> v (arr, arr + sizeof(arr) / sizeof(arr[0]) );
vector<int>::iterator i;
const int step = 2;
const int first = 0;
const int last = v.size()-1;
int counter=first;
for (i = v.begin()+first; counter<last; i+=step, counter+=step) {
    // Do something with *i
    cout << *i << endl;
}

Prints:

16
77

In this code, a counter is needed to track the position because not all iterators can do this.

Zenul_Abidin
  • 573
  • 8
  • 23
0

It is possible to use slices with std::valarray. Which is an STL analogue of numpy.array in python. It support different vectorized operations like min, max, +,-, *, /, etc. More info here.

std::slice(start, length, stride) allows to select and modify slices of an array without copying (documentation here).

The slicing would look like this:

  std::valarray<int> foo (9);
  for (int i=0; i<9; ++i) foo[i]=i;         //  0  1  2  3  4  5  6  7  8
                                            //     |  |  |  |  |
  std::slice myslice=std::slice(1,5,1);     //     v  v  v  v  v
  foo[myslice] *= std::valarray<int>(10,3); //  0 10 20 30 40 50  6  7  8

Or with stride=2:

  std::valarray<int> foo (9);
  for (int i=0; i<9; ++i) foo[i]=i;         //  0  1  2  3  4  5  6  7  8
                                            //     |     |     |
  std::slice myslice=std::slice(1,3,2);     //     v     v     v
  foo[myslice] *= std::valarray<int>(10,3); //  0 10  2 30  4 50  6  7  8
                                            //  |        |        |
  foo[std::slice (0,3,3)] = 99;             //  v        v        v
                                            // 99 10  2 99  4 50 99  7  8
  std::cout << "foo:";
  for (std::size_t n=0; n<foo.size(); n++)
      std::cout << ' ' << foo[n];
  std::cout << '\n';
Temak
  • 2,929
  • 36
  • 49