83

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}
Donald Duck
  • 8,409
  • 22
  • 75
  • 99
Wizard
  • 1,533
  • 4
  • 19
  • 32

6 Answers6

135

This can easily be done using std::vector's copy constructor:

v2 = std::vector<int>(v1.begin() + 1, v1.end());
DimChtz
  • 4,043
  • 2
  • 21
  • 39
14

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

mnesarco
  • 2,619
  • 23
  • 31
  • This slice syntax is pretty cool. But why not use `std::slice`? – Ehtesh Choudhury Oct 28 '21 at 18:43
  • 1
    std::slice is ok, but I was trying something similar to python syntax for std::vector. As I understand std::slice does not work with std::vector. (previous comment was truncated) – mnesarco Oct 29 '21 at 01:18
8

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';  
kunigami
  • 3,046
  • 4
  • 26
  • 42
xskxzr
  • 12,442
  • 12
  • 37
  • 77
6

I know it's late but have a look at valarray and its slices. If you are using a vector of some sort of NumericType, then it's worth giving it a try.

Adrian
  • 755
  • 9
  • 17
1

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;
}
rmbianchi
  • 6,241
  • 6
  • 26
  • 27
  • I would say this way of doing it is inefficient compared with the solution above, since `erase` will delete the first element and then relocate all the others. So then you first perform a copy of N elements, then delete one, then move N-1. – Victor Buendía Oct 12 '21 at 16:31
  • I never said it is efficient. I shared this as another way of doing it. – Sadaananth Anbucheliyan Oct 13 '21 at 01:20
-1

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.

uuu777
  • 765
  • 4
  • 21
  • The idea is reasonable, but C++ already has this packaged as `std::span`. xskxzr added an answer showing how to use it. – MSalters Nov 15 '22 at 13:42