SunnyIsaLearner
SunnyIsaLearner

Reputation: 780

Reshape 2D Vector in C++

I would like to implement a function that like the input of a 2d vector (a matrix), contain its origin elements, and return a reshaped version based on the input for number of columns and rows. The reshaped matrix need to be filled with all the elements of the original matrix in the same row-traversing order as they were.

I completed the following code:

vector<vector<int>> matrixReshape(vector<vector<int>>& nums, int r, int c) {
    int r_old = nums.size();
    int c_old = nums[0].size();


    if(r*c != r_old*c_old) return nums;

   vector<vector<int>> new_nums;
   new_nums.resize(r);

   // get the new matrix proper size
   for (int i =0; i< r; i++){
       new_nums[i].resize(c);
   } 

   int c_curr =0;
   int r_curr =0;

   // assign element
   for (int i = 0; i < r_old; i ++){
       for (int j =0; j < c_old; j++){

           if(c_curr == c){
               r_curr++;
               c_curr =0;
           }
           new_nums[r_curr][c_curr] = nums[i][j];
           c_curr++;
       }
   }
   return new_nums;
}

However, I feel like there must be a better way of doing it. I search stackoverflow and found several questions

How can I resize a 2D C++ vector?

How can I resize a 2D vector of objects given the width and height?

However, those answers all "reset" the current matrix which is not what I want. So my question is, is there a faster way to generate a 2d vector from a 2d vector by re-arranging its elements into the desired new shape?

Upvotes: 1

Views: 5563

Answers (1)

user4581301
user4581301

Reputation: 33932

Decided to follow my comment through. This answer heads pretty far into X-Y territory, but it should simplify things a great deal

#include <iostream>
#include <iomanip>
#include <vector>
#include <exception>

// wrapping class for 2D matrixes    
class Matrix
{
private:
    size_t rows, columns; // large, unsigned datatype. Don't want negative
                          // indices, so why allow them?
    std::vector<int> matrix; // 1D vector. Simple and easy to handle.
                             // also often much faster than vector of vectors 
                             // due to improved spatial locality helping 
                             // predictability of data access
public:
    // catchable exception should user request impossible dimension transformation
    class BadDimsException: public std::exception
    {
    public:
        const char* what() const noexcept
        {
            return "Invalid dimensions specified";
        }
    };

    // build zero-filled Matrix
    Matrix(size_t numrows, size_t numcols) :
            rows(numrows), columns(numcols), matrix(rows * columns)
    {
    }

    // build Matrix based on another Matrix with convertable dimensions
    // All of the heavy lifting is performed in the member initializer list
    // by simply copying data store of source Matrix
    // if matrix cannot be transformed, the thrown exception leaves user with 
    // nothing to work with and no chance of trying to continue with an un-
    // transformed matrix
    Matrix(size_t numrows, size_t numcols, const Matrix & source) :
            rows(numrows), columns(numcols), matrix(source.matrix)
    {
        if (rows * columns != source.rows * source.columns)
        { // Bad dimensions. Blow up.
            throw BadDimsException();
        }
    }

    // 2D to 1D mapping accessor
    int & operator()(size_t row, size_t column)
    {
        // check bounds here
        return matrix[row * columns + column]; 
    }

    // 2D to 1D mapping accessor for constant Matrix 
    int operator()(size_t row, size_t column) const
    {
        // check bounds here
        return matrix[row * columns + column];
    }

    // dimension accessors
    size_t getRows() const
    {
        return rows;
    }
    size_t getColumns() const
    {
        return columns;
    }
};


// stream formatter
std::ostream & operator<<(std::ostream & out, const Matrix & mat)
{
    for (size_t row = 0; row < mat.getRows(); ++row)
    {
        for (size_t col = 0; col < mat.getColumns(); ++col)
        {
            std::cout << std::setw(5) << mat(row, col);
        }
        std::cout << '\n';
    }
    return out;
}

And to test/demonstrate usage:

int main()
{
    Matrix one(2, 6); // make 2x6 matrix
    int count = 0;

    // set inputs to make errors really stand out
    for (size_t row = 0; row < one.getRows(); ++row)
    {
        for (size_t col = 0; col < one.getColumns(); ++col)
        {
            one(row, col) = count++;
        }
    }

    // print initial matrix
    std::cout << one << '\n';

    // make reshaped matrix
    Matrix two(3,4, one);

    //print reshaped Matrix
    std::cout << two << '\n';
    try
    {
        // make invalid reshaped matrix
        Matrix three(3, 3, one);

        // This should never print
        std::cout << three << '\n';
    }
    catch (const Matrix::BadDimsException & bde)
    {
        // Yay! Caught error!
        std::cout << bde.what() << '\n';
    }
}

Upvotes: 1

Related Questions