0

What I'm trying to achieve is getting only a portion of a rectangular array made using vectors of vectors of integers, with the result also being rectangular.

I already managed something similar using a 1D vector of integers, using this function:

typedef std::vector<int> int1D;
typedef std::vector<int1D> int2D;

int1D Copy1D(int1D array, int startIndex, int endIndex)
{
    return int1D(array.begin() + startIndex, array.begin() + endIndex + 1);
}

However, I can't seem to use the same approach for a 2D version of this function, as it then ends up selecting all the elements of the 1D vectors that are in the chosen range, but can't select the range chosen for within the 1D vectors.

I'm also open to trying out using a 1D vector as a 2D vector as described here, even more so if it is better in any way. However, for the sake of having options, I'd like efficient ways of doing both approaches.

  • do you need a copy, or do you just need a way to access elements of the subarray via some `x[i][j]` such that `x[i][j] = original[i+startindexi][j + startindexj]` ? – 463035818_is_not_an_ai Mar 06 '23 at 12:28
  • "However, I can't seem to use the same approach ..." you can use the same approach. There is just no constructor for the nested vector that you can use of out the box. But what you code does is just copying elements to a new vector, and you can do the same in 2d – 463035818_is_not_an_ai Mar 06 '23 at 12:30
  • opencv handles this efficiently, without actually copying when creating a sub matrix. I'm sure other libraries got good solutions as well. Why do you not use such a library ? – wohlstad Mar 06 '23 at 12:31
  • Pointless dynamic creation of copies of subsets of large static data is one of the popular ways how to achieve that C++ program performs worse than Python or JavaScript. – Öö Tiib Mar 06 '23 at 12:36
  • @463035818_is_not_a_number yes, it's fine as long as the result can be used as the same type as the type used in the function's parameter. Making a copy isn't necessary – Abdullah Jaffer Mar 06 '23 at 13:12
  • @wohlstad this looks like it will work quite nicely. Will try using this instead – Abdullah Jaffer Mar 06 '23 at 13:20

2 Answers2

2

If we look at the "2D" vector as a matrix, with rows and columns, then you want to get a sub-matrix, from row/column x1,y1 to row/column x2,y2, if I understand you correctly.

Then it's no easy way to do that in a simple way. You have to explicitly create the resulting matrix (you can preallocate memory for it since you know the dimensions) and the explicitly copy each row one by one.

As another possible solution, you could create a view of the wanted part of the matrix. This view then holds a reference to the original matrix, and its subscript operator will then map the index into an index in the actual matrix. So for example index 0 becomes x1.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
0

If you can use C++20, you can use ranges to create views into your data. The following code will work with any two-dimensional data, i.e. anything that you can iterate and has elements that you can iterate over again.

#include <ranges>

auto rectangular_view(auto&& range, int istart, int iend, int jstart, int jend)
{
    return std::views::iota(istart, iend+1) 
        | std::views::transform(
            [&range, jstart, jend](int i){
                return range[i] 
                    | std::views::drop(jstart) 
                    | std::views::take(jend-jstart + 1);
            }
        );
};

Let me walk you through the steps:

  • std::views::iota(istart, iend+1) will create a range of integers istart, istart + 1 , ... , iend. This range represents the indices of the rows you want to keep.
  • std::views::transform will create a new range from this range of integers, where a function is applied to each integer.
  • The function passed to std::views::transform should return the elements to keep in each row. This function is implemented by the lambda [&range, jstart, jend](int i) { ... }. It binds the variable range by reference and copies the variables jstart and jend from the outer scope. It takes an integer i as input.
  • The lambda returns the ith row of the input range, drops the first jstart elements and takes jend - start + 1 elements. Thus, it returns all elements to keep in the i-th row.

You can access the view like so:

#include <vector>
#include <iostream>

int main()
{
    int2D v = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

    for (auto row : rectangular_view(v, 0, 2, 0, 1)) {
        for (auto e : row) {
            std::cout << e << " ";
        }
        std::cout << "\n";
    }

    return 0;
}

Output:

1 2 
4 5 
7 8 

https://godbolt.org/z/3G5GzePEa

joergbrech
  • 2,056
  • 1
  • 5
  • 17