An̲̳̳drew
An̲̳̳drew

Reputation: 13872

Best way to extract a subvector from a vector?

Suppose I have a std::vector (let's call it myVec) of size N. What's the simplest way to construct a new vector consisting of a copy of elements X through Y, where 0 <= X <= Y <= N-1? For example, myVec [100000] through myVec [100999] in a vector of size 150000.

If this cannot be done efficiently with a vector, is there another STL datatype that I should use instead?

Upvotes: 410

Views: 589335

Answers (18)

einpoklum
einpoklum

Reputation: 131405

These days, we use spans! So you would write:

#include <span>

...
auto start_pos = 100000;
auto length = 1000;
auto span_of_myvec = std::span(myvec);
auto my_subspan = span_of_myvec.subspan(start_pos, length);

to get a span of 1000 elements of the same type as myvec's. Or a more terse form:

auto my_subspan = std::span(myvec).subspan(1000000, 1000);

(but I don't like this as much, since the meaning of each numeric argument is not entirely clear; and it gets worse if the length and start_pos are of the same order of magnitude.)

Anyway, remember that this is not a copy, it's just a view of the data in the vector, so be careful. If you want an actual copy, you could do:

std::vector<T> new_vec(my_subspan.cbegin(), my_subspan.cend());

Notes:

Upvotes: 36

D&#225;vid T&#243;th
D&#225;vid T&#243;th

Reputation: 3235

This discussion is pretty old, but the simplest one isn't mentioned yet, with list-initialization:

 vector<int> subvector = {big_vector.begin() + 3, big_vector.end() - 2}; 

It requires c++11 or above.

Example usage:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main(){

    vector<int> big_vector = {5,12,4,6,7,8,9,9,31,1,1,5,76,78,8};
    vector<int> subvector = {big_vector.begin() + 3, big_vector.end() - 2};

    cout << "Big vector: ";
    for_each(big_vector.begin(), big_vector.end(),[](int number){cout << number << ";";});
    cout << endl << "Subvector: ";
    for_each(subvector.begin(), subvector.end(),[](int number){cout << number << ";";});
    cout << endl;
}

Result:

Big vector: 5;12;4;6;7;8;9;9;31;1;1;5;76;78;8;
Subvector: 6;7;8;9;9;31;1;1;5;76;

Note: the extent of the range is defined to take the second iterator as exclusive. e.g. in the above example the last element in the subvector is 76, which is 3 positions before big_vector.end()

Upvotes: 68

Ludovic Aubert
Ludovic Aubert

Reputation: 10528

You can use a combination of views::drop() and views::take():

#include <vector>
#include <string>
#include <ranges>
#include <format>
using namespace std;
using namespace std::ranges;

int main(){
    const vector<int> v = views::iota(0,10) | ranges::to<vector>();

    const vector<int> sub = v | views::drop(3) | views::take(4) | ranges::to<vector>() ;

    const string buffer = sub |
                views::transform([](int i){return format("{}",i);}) | 
                views::join_with(',') | 
                ranges::to<string>();

    printf("%s", buffer.c_str());
    return 0;
}

enter image description here

Upvotes: 0

igormcoelho
igormcoelho

Reputation: 99

Just to add another recent (C++14 onwards) option to the table, for a similar situation we created a subvector class (see igormcoelho/view_wrapper project). The subvector creates a constant time O(1) range view to a vector class, that can also generate a copy with method as_copy:

using view_wrapper::subvector;
// creates vector with 150000 elements
std::vector<int> myVec(150000, 0);
// get range interval [100000, 100999) in O(1) constant time
subvector<int> v(myVec, 100000, 100999); 
// make copy in O(M) time, where M=100999-100000
std::vector<int> v2 = v.as_copy()

The coolest thing on subvector is that, as a range, one can change it and this will have an effect on the original vector:

// add 1 in the end of the subvector v and in the middle of myVec
v.push_back(1);
// size of myVec is now one more
assert(myVec.size() == 150001);

Note that a subvector push_back operation will in fact occur as an emplace in the middle of the original vector, so it may cost O(N), and not O(1) amortized cost of typical push_back.

Finally, if your compiler supports c++20 you can easy convert subvector into a std::span with method as_span():

std::span sp = v.as_span();

Upvotes: 0

tangmr
tangmr

Reputation: 1

vector::assign may be another solution

// note: size1 < src.size() && size2 < src.size()
std::vector<int> sub1(size1), sub2(size2);
sub1.assign(src.begin(), src.begin() + size1);
sub2.assign(src.begin(), src.begin() + size2);

Upvotes: 0

Meshu Deb Nath
Meshu Deb Nath

Reputation: 131

Suppose there are two vectors.

 vector<int> vect1{1, 2, 3, 4};
 vector<int> vect2;

Method 1. Using copy function. copy(first_iterator_index, last_iterator_index, back_inserter()) :- This function takes 3 arguments, firstly, the first iterator of old vector. Secondly, the last iterator of old vector and third is back_inserter function to insert values from back.

    // Copying vector by copy function
    copy(vect1.begin(), vect1.end(), back_inserter(vect2));

Method 2. By using Assign Function. assign(first_iterator_o, last_iterator_o). This method assigns the same values to new vector as old one. This takes 2 arguments, first iterator to old vector and last iterator to old vector.

    //Copying vector by assign function
    vect2.assign(vect1.begin(), vect1.end());

Upvotes: 2

Anteru
Anteru

Reputation: 19374

std::vector<T>(input_iterator, input_iterator), in your case foo = std::vector<T>(myVec.begin () + 100000, myVec.begin () + 150000);, see for example here

Upvotes: 32

You could just use insert

vector<type> myVec { n_elements };

vector<type> newVec;

newVec.insert(newVec.begin(), myVec.begin() + X, myVec.begin() + Y);

Upvotes: 5

JHBonarius
JHBonarius

Reputation: 11261

Yet another option: Useful for instance when moving between a thrust::device_vector and a thrust::host_vector, where you cannot use the constructor.

std::vector<T> newVector;
newVector.reserve(1000);
std::copy_n(&vec[100000], 1000, std::back_inserter(newVector));

Should also be complexity O(N)

You could combine this with top anwer code

vector<T>::const_iterator first = myVec.begin() + 100000;
vector<T>::const_iterator last = myVec.begin() + 101000;
std::copy(first, last, std::back_inserter(newVector));

Upvotes: 0

Omega Alpha
Omega Alpha

Reputation: 1

Copy elements from one vector to another easily
In this example, I am using a vector of pairs to make it easy to understand
`

vector<pair<int, int> > v(n);

//we want half of elements in vector a and another half in vector b
vector<pair<lli, lli> > a(v.begin(),v.begin()+n/2);
vector<pair<lli, lli> > b(v.begin()+n/2, v.end());


//if v = [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
//then a = [(1, 2), (2, 3)]
//and b = [(3, 4), (4, 5), (5, 6)]

//if v = [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7)]
//then a = [(1, 2), (2, 3), (3, 4)]
//and b = [(4, 5), (5, 6), (6, 7)]

'
As you can see you can easily copy elements from one vector to another, if you want to copy elements from index 10 to 16 for example then we would use

vector<pair<int, int> > a(v.begin()+10, v.begin+16);

and if you want elements from index 10 to some index from end, then in that case

vector<pair<int, int> > a(v.begin()+10, v.end()-5);

hope this helps, just remember in the last case v.end()-5 > v.begin()+10

Upvotes: 0

myd7349
myd7349

Reputation: 1

Maybe the array_view/span in the GSL library is a good option.

Here is also a single file implementation: array_view.

Upvotes: 0

MasterHD
MasterHD

Reputation: 2384

You didn't mention what type std::vector<...> myVec is, but if it's a simple type or struct/class that doesn't include pointers, and you want the best efficiency, then you can do a direct memory copy (which I think will be faster than the other answers provided). Here is a general example for std::vector<type> myVec where type in this case is int:

typedef int type; //choose your custom type/struct/class
int iFirst = 100000; //first index to copy
int iLast = 101000; //last index + 1
int iLen = iLast - iFirst;
std::vector<type> newVec;
newVec.resize(iLen); //pre-allocate the space needed to write the data directly
memcpy(&newVec[0], &myVec[iFirst], iLen*sizeof(type)); //write directly to destination buffer from source buffer

Upvotes: 6

mrrgu
mrrgu

Reputation: 17

Posting this late just for others..I bet the first coder is done by now. For simple datatypes no copy is needed, just revert to good old C code methods.

std::vector <int>   myVec;
int *p;
// Add some data here and set start, then
p=myVec.data()+start;

Then pass the pointer p and a len to anything needing a subvector.

notelen must be!! len < myVec.size()-start

Upvotes: -2

Eclipse
Eclipse

Reputation: 45493

If both are not going to be modified (no adding/deleting items - modifying existing ones is fine as long as you pay heed to threading issues), you can simply pass around data.begin() + 100000 and data.begin() + 101000, and pretend that they are the begin() and end() of a smaller vector.

Or, since vector storage is guaranteed to be contiguous, you can simply pass around a 1000 item array:

T *arrayOfT = &data[0] + 100000;
size_t arrayOfTLength = 1000;

Both these techniques take constant time, but require that the length of data doesn't increase, triggering a reallocation.

Upvotes: 12

Loki Astari
Loki Astari

Reputation: 264331

Just use the vector constructor.

std::vector<int>   data();
// Load Z elements into data so that Z > Y > X

std::vector<int>   sub(&data[100000],&data[101000]);

Upvotes: 113

Daniel Spiewak
Daniel Spiewak

Reputation: 55115

The only way to project a collection that is not linear time is to do so lazily, where the resulting "vector" is actually a subtype which delegates to the original collection. For example, Scala's List#subseq method create a sub-sequence in constant time. However, this only works if the collection is immutable and if the underlying language sports garbage collection.

Upvotes: 1

Greg Rogers
Greg Rogers

Reputation: 36429

vector<T>::const_iterator first = myVec.begin() + 100000;
vector<T>::const_iterator last = myVec.begin() + 101000;
vector<T> newVec(first, last);

It's an O(N) operation to construct the new vector, but there isn't really a better way.

Upvotes: 491

Yuval F
Yuval F

Reputation: 20621

You can use STL copy with O(M) performance when M is the size of the subvector.

Upvotes: 3

Related Questions