Slicing a vector in C++
Asked Answered
E

7

90

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}
Enugu answered 27/5, 2018 at 6:30 Comment(6)
v2 = std::vector<int>(v1.begin() + 1, v1.end());Obola
@Obola -- make that an answer.Gorilla
@juanchopanza I'll explain it in the post moreEnugu
do you want to copy or reference the elements?Epicedium
Does this answer your question? Best way to extract a subvector from a vector?Arrhythmia
All these answers give no information into when it's good to use which approach and what the differences are.Gadoid
O
144

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

v2 = std::vector<int>(v1.begin() + 1, v1.end());
Obola answered 27/5, 2018 at 6:33 Comment(9)
This answer could be improved mentioning whether v1.end() is included or not.Brigette
@AlessandroFlati could you explain what you mean?Wesson
Mh, trying to rephrase. Does v2 contain v1.end(), the element at the end of the first vector?Brigette
@AlessandroFlati Yes, it does. Otherwise my answer would be wrong since OP asked: "I simply want to get all but the first element from a vector" :)Obola
I know it does, I just suggested to include it in the answer to make it flawless.Brigette
Just for the record, v1.end() is not the element at the end of the vector, it is the iterator that points at the (non-existent) element after the last element.Feast
Is the Time Complexity O(size of sliced vector) ?Penelopepeneplain
@Penelopepeneplain Affirmative.Lillielilliputian
Please remove that last sentence. As @Feast mentioned, v1.end() does not point to the last element in the vector. So no, v2 won't contain v1.end().Hb
G
16

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

Gautier answered 28/7, 2021 at 23:27 Comment(2)
This slice syntax is pretty cool. But why not use std::slice?Custos
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)Gautier
T
15

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';  
Titulary answered 16/7, 2021 at 2:28 Comment(1)
One can also use std::span::subspan() with span. It doesn’t support negative index, though.Mho
C
8

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.

Crocidolite answered 13/3, 2019 at 14:54 Comment(1)
https://mcmap.net/q/128027/-c-valarray-vs-vectorParesh
P
2

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;
}
Phonsa answered 27/5, 2018 at 6:42 Comment(2)
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.Skeg
I never said it is efficient. I shared this as another way of doing it.Phonsa
S
0

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.

Scirrhous answered 23/11, 2023 at 14:50 Comment(0)
P
-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.

Plunder answered 31/7, 2020 at 20:44 Comment(1)
The idea is reasonable, but C++ already has this packaged as std::span. xskxzr added an answer showing how to use it.Serotherapy

© 2022 - 2024 — McMap. All rights reserved.