Reputation: 1663
Is there an equivalent of list slicing [1:]
from Python in C++ with vectors? I simply want to get all but the first element from a vector.
Python's list slicing operator:
list1 = [1, 2, 3]
list2 = list1[1:]
print(list2) # [2, 3]
C++ Desired result:
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2;
v2 = v1[1:];
std::cout << v2 << std::endl; //{2, 3}
Upvotes: 95
Views: 112905
Reputation: 4333
This can easily be done using std::vector
's copy constructor:
v2 = std::vector<int>(v1.begin() + 1, v1.end());
However please note that this will make a copy of the existing elements. For a large array this may be slow, and there is additional overhead due to the additional allocation required.
If you do not need a copy, and simply want to read but not modify the elements, use a std::span
instead.
Upvotes: 151
Reputation: 9
To find a sub vector from index a to b, then, simply do this:
vector<int> sub_vec(v.begin() + a, v.begin() + b + 1);
This will create a Sub Vector from the a
index to the b
index and we also add 1 in the end of the range while slicing as because end index will be excluded (value b-1
will be taken as the last index). So, we add 1 to include our last index also.
Upvotes: 0
Reputation: 13040
It depends on whether you want a view or a copy.
Python's slicing for lists copies references to the elements, so it cannot be simply regarded as a view or a copy. For example,
list1 = [1, 2, 3]
list2 = list1[1:]
list2[1] = 5
print(list1) # does not change, still [1, 2, 3]
list1 = [1, 2, [3]]
list2 = list1[1:]
list2[1][0] = 5
print(list1) # changes, becomes [1, 2, [5]]
See this post for details.
DimChtz's anwer models the copy situation. If you just want a view, in C++20, you can use ranges (besides std::views::drop
, std::views::take
and std::views::counted
are also useful):
auto v2 = v1 | std::views::drop(1); // #include <ranges>
for (auto &e: v2) std::cout << e << '\n';
or std::span
:
std::span v2{v1.begin() + 1, v1.end()}; // #include <span>
for (auto &e: v2) std::cout << e << '\n';
Upvotes: 16
Reputation: 2778
In C++20 it is pretty easy:
#include <span>
#include <vector>
#include <iostream>
template<int left = 0, int right = 0, typename T>
constexpr auto slice(T&& container)
{
if constexpr (right > 0)
{
return std::span(begin(std::forward<T>(container))+left, begin(std::forward<T>(container))+right);
}
else
{
return std::span(begin(std::forward<T>(container))+left, end(std::forward<T>(container))+right);
}
}
int main()
{
std::vector v{1,2,3,4,5,6,7,8,9};
std::cout << "-------------------" << std::endl;
auto v0 = slice<1,0>(v);
for (auto i : v0)
{
std::cout << i << std::endl;
}
std::cout << "-------------------" << std::endl;
auto v1 = slice<0,-1>(v);
for (auto i : v1)
{
std::cout << i << std::endl;
}
std::cout << "-------------------" << std::endl;
auto v2 = slice<1,3>(v);
for (auto i : v2)
{
std::cout << i << std::endl;
}
std::cout << "-------------------" << std::endl;
auto v3 = slice<1,-1>(v);
for (auto i : v3)
{
std::cout << i << std::endl;
}
std::cout << "-------------------" << std::endl;
auto v4 = slice<3,3>(v);
for (auto i : v4)
{
std::cout << i << std::endl;
}
}
Result:
Program returned: 0
-------------------
2
3
4
5
6
7
8
9
-------------------
1
2
3
4
5
6
7
8
-------------------
2
3
-------------------
2
3
4
5
6
7
8
-------------------
You can also add boundary checks and other cases like negative left indices etc... but this is only an example.
Run in compiler explorer: https://godbolt.org/z/qeaxvjdbj
Upvotes: 19
Reputation: 901
It seems that the cheapest way is to use pointer to the starting element and the number of elements in the slice. It would not be a real vector but it will be good enough for many uses.
Upvotes: -1
Reputation: 462
You can follow the above answer. It's always better to know multiple ways.
int main
{
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2{v1};
v2.erase( v2.begin() );
return 0;
}
Upvotes: 1