├── .babelrc ├── .gitignore ├── README.md ├── build ├── index.cjs.js └── index.es.js ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── arrayUtils.js ├── getDiagonals.js ├── getSpiral.js ├── index.js ├── matrixDecorators.js ├── matrixDimensions.js ├── matrixShift.js ├── matrixSlice.js └── transpose.js └── test ├── applyToEntries.test.js ├── getDiagonals.test.js ├── getSpirals.test.js ├── globals.js ├── matrixShift.test.js ├── transpose.test.js └── utilities.test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # matrix-magic 2 | 3 | ``` 4 | npm install --save matrix-magic 5 | ``` 6 | 7 | This library performs various transformations on matrices. This library does not do matrix operations (or more specifically, matrix multiplication), but serves to manipulate the shape of a matrix. It does stuff like this: 8 | 9 | ``` 10 | getMajorDiagonals() 11 | 12 | [[a, b, c], [[e], 13 | [d, a, b], --> [d, d], 14 | [e, d, a]] [a, a, a], 15 | [b, b], 16 | [c]] 17 | ``` 18 | 19 | Here's another fun one: 20 | 21 | ``` 22 | getClockwiseSpiral() 23 | 24 | [[1, 2, 3], 25 | [8, 9, 4], --> [1, 2, 3, 4, 5, 6, 7, 8, 9] 26 | [7, 6, 5]] 27 | 28 | ``` 29 | 30 | It also provides some utilities to work with matrices: 31 | 32 | ``` 33 | transpose() 34 | 35 | [[a, b, c], [[a, a, a], 36 | [a, b, c], --> [b, b, b], 37 | [a, b, c]] [c, c, c]] 38 | ``` 39 | 40 | ``` 41 | getMiddleCols() 42 | 43 | [[*, 1, 2, 3, *], [[1, 2, 3], 44 | [*, 4, 5, 6, *], --> [4, 5, 6], 45 | [*, 7, 8, 9, *]] [7, 8, 9]] 46 | 47 | ``` 48 | 49 | # Library 50 | 51 | Note that in the following functions, `matrix` is an array of arrays, where each sub-array has the same length. Most of these functions will throw an error if the input is not a matrix. 52 | 53 | ## Matrix Utilities [<>](https://github.com/trainorpj/matrix-magic/blob/master/src/matrixDimensions.js) 54 | 55 | ### `getMatrixWidth(matrix)` 56 | 57 | Returns the width of `matrix` i.e. the number of columns. 58 | 59 | ### `getMatrixHeight(matrix)` 60 | 61 | Returns the height of `matrix` i.e. the number of rows. 62 | 63 | ### `getMatrixDimensions(matrix)` 64 | 65 | Returns and object `{width, height}`, where `width` is the number of columns in `matrix`, and `height` is the number of rows in `matrix`. 66 | 67 | ## Row Utilities [<>](https://github.com/trainorpj/matrix-magic/blob/master/src/matrixSlice.js) 68 | 69 | ### `sliceMatrixRows(matrix, start, stop)` 70 | 71 | Returns the slice of the rows of `matrix` from index `start` to index `stop - 1`, much like `Array.prototype.slice()`. 72 | 73 | ### `getTopRow(matrix)` 74 | 75 | Returns the top row of `matrix` as a matrix. 76 | 77 | ### `getAllButTopRow(matrix)` 78 | 79 | Returns `matrix` without the top row. 80 | 81 | ### `getBottomRow(matrix)` 82 | 83 | Returns the bottom row of `matrix` as a matrix. 84 | 85 | ### `getAllButBottomRow(matrix)` 86 | 87 | Returns `matrix` without the bottom row. 88 | 89 | ### `getMiddleRows(matrix)` 90 | 91 | Returns `matrix` without top and bottom rows. 92 | 93 | ### `flipRows(matrix)` [<>](https://github.com/trainorpj/matrix-magic/blob/master/src/matrixShift.js) 94 | 95 | Returns `matrix` with the rows in reverse order. 96 | 97 | ## Column Utilities [<>](https://github.com/trainorpj/matrix-magic/blob/master/src/matrixSlice.js) 98 | 99 | ### `sliceMatrixCols(matrix, start, stop)` 100 | 101 | Returns the slice of the columns of `matrix` from index `start` to index `stop - 1`, much like `Array.prototype.slice()`. 102 | 103 | ### `getLeftCol(matrix)` 104 | 105 | Returns the left column of `matrix` as a matrix. 106 | 107 | ### `getRightCol(matrix)` 108 | 109 | Returns the right column of `matrix as a matrix. 110 | 111 | ### `getAllButLeftCol(matrix)` 112 | 113 | Returns `matrix` without the left column. 114 | 115 | ### `getAllButRightCol(matrix)` 116 | 117 | Returns `matrix` without the right column. 118 | 119 | ### `getMiddleCols(matrix)` 120 | 121 | Returns `matrix` without the left and right columns. 122 | 123 | ### `flipCols(matrix)` [<>](https://github.com/trainorpj/matrix-magic/blob/master/src/matrixShift.js) 124 | 125 | Returns `matrix` with the columns in reverse order. 126 | 127 | ## Matrix Utilities 128 | 129 | ### `transpose(matrix)` [<>](https://github.com/trainorpj/matrix-magic/blob/master/src/transpose.js) 130 | 131 | Returns `matrix` with the rows and the columns flipped, i.e. the leftmost column is now the top row (and vice versa), and so on. 132 | 133 | ### `getMinorDiagonals(matrix)` [<>](https://github.com/trainorpj/matrix-magic/blob/master/src/getDiagonals.js) 134 | 135 | Returns an nArray (an array of arrays, not necessarily of equal length) of the minor diagonals of `matrix`, starting from the top-left corner, down to the bottom-right corner. 136 | 137 | ### `getMajorDiagonals(matrix)` [<>](https://github.com/trainorpj/matrix-magic/blob/master/src/getDiagonals.js) 138 | 139 | Returns an nArray (an array of arrays, not necessarily of equal length) of the major diagonals of `matrix`, starting from the bottom-left corner, up to the top-right corner. 140 | 141 | ### `getClockwiseSpiral(matrix)` [<>](https://github.com/trainorpj/matrix-magic/blob/master/src/getSpiral.js) 142 | 143 | Returns an array of the entries of `matrix` going clockwise from the top-left corner, spiraling into the center. 144 | 145 | ### `getCounterClockwiseSpiral(matrix)` [<>](https://github.com/trainorpj/matrix-magic/blob/master/src/getSpiral.js) 146 | 147 | Returns an array of entries of `matrix` going counter-clockwise from the top-left corner, spiraling into the center. 148 | 149 | ## Decorators [<>](https://github.com/trainorpj/matrix-magic/blob/master/src/matrixDecorators.js) 150 | 151 | ### `doColumnOperation(fcn)(matrix, ...args)` 152 | 153 | `doColumnOperation(fcn)` returns a function that transposes `matrix`, applies `fcn`, and then returns the resulting transpose. 154 | 155 | This utility allows us to use row utilities on the columns. The definition is 156 | 157 | ```js 158 | const doColumnOperation = fcn => (mtx, ...args) => { 159 | return transpose(fcn(transpose(mtx), ...args)); 160 | }; 161 | ``` 162 | 163 | ### `doMatrixCheck(fcn)(nArray, ...args)` 164 | 165 | `doMatrixCheck(fcn)` returns a function that evaluates `fcn(nArray, ...args)` if `nArray` is a matrix. If `nArray` is not a matrix, it will throw an error. 166 | 167 | ### `matrixWrapper(fcn)(nArray, ...args)` 168 | 169 | `matrixWrapper(fcn)` returns a function that evaluates `fcn(nArray, ...args)` pending several checks. Those checks are: 170 | * Check if it is the empty matrix `[[]]` 171 | * Check if it is a matrix with `doMatrixCheck` 172 | 173 | 174 | 175 | 176 | # About 177 | 178 | This library has no dependencies and consists of entirely pure functions. I mostly did this because I thought it would be fun (and it is!), and to learn about unit testing with jest. 179 | 180 | If you'd like to contribute, then by all means, go ahead! Here are ways you can help out: 181 | 182 | * Make snippets like the ones I made above to show what each function is doing. 183 | * Contribute to the documentation! Describe what each function does (related to providing examples). 184 | * Write a test! This would be a big help, and would be a great way to get into testing if you have yet to try it (and you *should* be testing your code). 185 | * Re-write a test. Just because it's there doesn't mean it's a good test. 186 | * Make a pull request for a new feature. This might be a little harder, as I'd like to adhere to a purely functional style. Talk to me if you're interested in adding something. 187 | * Find and report bugs! 188 | -------------------------------------------------------------------------------- /build/index.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { value: true }); 4 | 5 | // Gets first element in array 6 | var getFirstElement = function getFirstElement(arr) { 7 | return arr[0]; 8 | }; 9 | 10 | // Gets all but first element in array 11 | var getAllButFirstElement = function getAllButFirstElement(arr) { 12 | return arr.slice(1); 13 | }; 14 | 15 | // Gets last element in array 16 | var getLastElement = function getLastElement(arr) { 17 | return arr[arr.length - 1]; 18 | }; 19 | 20 | // Gets all but the last element in array 21 | var getAllButLastElement = function getAllButLastElement(arr) { 22 | return arr.slice(0, arr.length - 1); 23 | }; 24 | 25 | // Gets middle elements in array 26 | var getMiddleElements = function getMiddleElements(arr) { 27 | return arr.slice(1, arr.length - 1); 28 | }; 29 | 30 | // Given an nArray, it filters out empty arrays 31 | var filterOutEmptyArrays = function filterOutEmptyArrays(nArray) { 32 | return nArray.filter(function (arr) { 33 | return arr.length > 0; 34 | }); 35 | }; 36 | 37 | var flattenArray = function flattenArray(nArray) { 38 | return nArray.reduce(function (acc, cur) { 39 | return acc.concat(cur); 40 | }, []); 41 | }; 42 | 43 | // Get height of matrix 44 | var getMatrixHeight$1 = function getMatrixHeight(mtx) { 45 | return mtx.length; 46 | }; 47 | 48 | // Get width of matrix 49 | var getMatrixWidth$1 = function getMatrixWidth(mtx) { 50 | return mtx.length > 0 ? mtx[0].length : 0; 51 | }; 52 | 53 | // Get matrix width and height 54 | // return {width, height} object 55 | var getMatrixDimensions$1 = function getMatrixDimensions(mtx) { 56 | var height = getMatrixHeight$1(mtx); 57 | var width = getMatrixWidth$1(mtx); 58 | 59 | return { width: width, height: height }; 60 | }; 61 | 62 | var asyncGenerator = function () { 63 | function AwaitValue(value) { 64 | this.value = value; 65 | } 66 | 67 | function AsyncGenerator(gen) { 68 | var front, back; 69 | 70 | function send(key, arg) { 71 | return new Promise(function (resolve, reject) { 72 | var request = { 73 | key: key, 74 | arg: arg, 75 | resolve: resolve, 76 | reject: reject, 77 | next: null 78 | }; 79 | 80 | if (back) { 81 | back = back.next = request; 82 | } else { 83 | front = back = request; 84 | resume(key, arg); 85 | } 86 | }); 87 | } 88 | 89 | function resume(key, arg) { 90 | try { 91 | var result = gen[key](arg); 92 | var value = result.value; 93 | 94 | if (value instanceof AwaitValue) { 95 | Promise.resolve(value.value).then(function (arg) { 96 | resume("next", arg); 97 | }, function (arg) { 98 | resume("throw", arg); 99 | }); 100 | } else { 101 | settle(result.done ? "return" : "normal", result.value); 102 | } 103 | } catch (err) { 104 | settle("throw", err); 105 | } 106 | } 107 | 108 | function settle(type, value) { 109 | switch (type) { 110 | case "return": 111 | front.resolve({ 112 | value: value, 113 | done: true 114 | }); 115 | break; 116 | 117 | case "throw": 118 | front.reject(value); 119 | break; 120 | 121 | default: 122 | front.resolve({ 123 | value: value, 124 | done: false 125 | }); 126 | break; 127 | } 128 | 129 | front = front.next; 130 | 131 | if (front) { 132 | resume(front.key, front.arg); 133 | } else { 134 | back = null; 135 | } 136 | } 137 | 138 | this._invoke = send; 139 | 140 | if (typeof gen.return !== "function") { 141 | this.return = undefined; 142 | } 143 | } 144 | 145 | if (typeof Symbol === "function" && Symbol.asyncIterator) { 146 | AsyncGenerator.prototype[Symbol.asyncIterator] = function () { 147 | return this; 148 | }; 149 | } 150 | 151 | AsyncGenerator.prototype.next = function (arg) { 152 | return this._invoke("next", arg); 153 | }; 154 | 155 | AsyncGenerator.prototype.throw = function (arg) { 156 | return this._invoke("throw", arg); 157 | }; 158 | 159 | AsyncGenerator.prototype.return = function (arg) { 160 | return this._invoke("return", arg); 161 | }; 162 | 163 | return { 164 | wrap: function (fn) { 165 | return function () { 166 | return new AsyncGenerator(fn.apply(this, arguments)); 167 | }; 168 | }, 169 | await: function (value) { 170 | return new AwaitValue(value); 171 | } 172 | }; 173 | }(); 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | var toConsumableArray = function (arr) { 224 | if (Array.isArray(arr)) { 225 | for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; 226 | 227 | return arr2; 228 | } else { 229 | return Array.from(arr); 230 | } 231 | }; 232 | 233 | // Transpose a matrix 234 | var transpose$1 = function transpose(mtx) { 235 | var width = getMatrixWidth$1(mtx); 236 | 237 | var leftCol = mtx.map(getFirstElement); 238 | 239 | if (width < 2) { 240 | return [leftCol]; 241 | } else { 242 | var rightCols = mtx.map(getAllButFirstElement); 243 | return [leftCol].concat(toConsumableArray(transpose(rightCols))); 244 | } 245 | }; 246 | 247 | // the empty matrix 248 | var emptyMatrix = [[]]; 249 | 250 | // check if matrix is empty 251 | var checkIfEmptyMatrix = function checkIfEmptyMatrix(mtx) { 252 | var isAnArray = Array.isArray(mtx); 253 | 254 | return !isAnArray ? false : mtx.length === 1 ? mtx[0].length === 0 : false; 255 | }; 256 | 257 | // boolean product, passed to a reducer 258 | var booleanProduct = function booleanProduct(acc, curr) { 259 | return Boolean(acc * curr); 260 | }; 261 | 262 | // check if nArray is a matrix 263 | var checkIfMatrix = function checkIfMatrix(nArray) { 264 | if (nArray.length === 0) { 265 | return true; 266 | } 267 | 268 | var checkIfArrays = nArray.map(function (d) { 269 | return Array.isArray(d); 270 | }).reduce(booleanProduct, true); 271 | 272 | if (checkIfArrays === false) { 273 | return false; 274 | } 275 | 276 | var firstRow = nArray[0]; 277 | var height = nArray.length; 278 | var width = firstRow.length; 279 | 280 | if (height < 2) { 281 | return true; 282 | } else { 283 | return nArray.map(function (d) { 284 | return d.length === width; 285 | }).reduce(booleanProduct, true); 286 | } 287 | }; 288 | 289 | // decorator to check if nArray is a matrix 290 | var doMatrixCheck = function doMatrixCheck(fcn) { 291 | return function (nArray) { 292 | for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 293 | args[_key - 1] = arguments[_key]; 294 | } 295 | 296 | if (checkIfMatrix(nArray) === false) { 297 | console.error("not a matrix"); 298 | throw new Error(); 299 | } else { 300 | return fcn.apply(undefined, [nArray].concat(args)); 301 | } 302 | }; 303 | }; 304 | 305 | // A decorator for any matrix operation: 306 | // * check if it's the identity 307 | // * check if it's a matrix 308 | // * apply a function 309 | var matrixWrapper = function matrixWrapper(fcn) { 310 | return function (nArray) { 311 | for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { 312 | args[_key2 - 1] = arguments[_key2]; 313 | } 314 | 315 | if (checkIfEmptyMatrix(nArray)) { 316 | return emptyMatrix; 317 | } else { 318 | return doMatrixCheck(fcn).apply(undefined, [nArray].concat(args)); 319 | } 320 | }; 321 | }; 322 | 323 | // transform matrix to column space, do operation, transpose back 324 | var doColumnOperation = function doColumnOperation(fcn) { 325 | return function (mtx) { 326 | for (var _len3 = arguments.length, args = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { 327 | args[_key3 - 1] = arguments[_key3]; 328 | } 329 | 330 | return transpose$1(fcn.apply(undefined, [transpose$1(mtx)].concat(args))); 331 | }; 332 | }; 333 | 334 | var applyToEntries$1 = function applyToEntries(mtx) { 335 | for (var _len4 = arguments.length, args = Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) { 336 | args[_key4 - 1] = arguments[_key4]; 337 | } 338 | 339 | return function (fcn) { 340 | return mtx.map(function (r) { 341 | return r.map(function (entry) { 342 | return fcn.apply(undefined, [entry].concat(args)); 343 | }); 344 | }); 345 | }; 346 | }; 347 | 348 | var flipRows$1 = function flipRows(mtx) { 349 | return mtx.slice().reverse(); 350 | }; 351 | 352 | var flipCols$1 = doColumnOperation(flipRows$1); 353 | 354 | // Slice rows from matrix 355 | var sliceMatrixRows$1 = function sliceMatrixRows(mtx, startIndex, stopIndex) { 356 | if (stopIndex === undefined) { 357 | return mtx.slice(startIndex); 358 | } else { 359 | return mtx.slice(startIndex, stopIndex); 360 | } 361 | }; 362 | 363 | // Get top row from matrix 364 | var getTopRow$1 = function getTopRow(mtx) { 365 | return [getFirstElement(mtx)]; 366 | }; 367 | 368 | // Get all but top row from matrix 369 | var getAllButTopRow$1 = function getAllButTopRow(mtx) { 370 | return getAllButFirstElement(mtx); 371 | }; 372 | 373 | // Get bottom row from matrix 374 | var getBottomRow$1 = function getBottomRow(mtx) { 375 | return [getLastElement(mtx)]; 376 | }; 377 | 378 | // Get all but bottom row from matrix 379 | var getAllButBottomRow$1 = function getAllButBottomRow(mtx) { 380 | return getAllButLastElement(mtx); 381 | }; 382 | 383 | // Get middle rows from matrix 384 | var getMiddleRows$1 = function getMiddleRows(mtx) { 385 | return getMiddleElements(mtx); 386 | }; 387 | 388 | // Slice columns from matrix 389 | var sliceMatrixCols$1 = doColumnOperation(sliceMatrixRows$1); 390 | 391 | // get left column from matrix 392 | var getLeftCol$1 = doColumnOperation(getTopRow$1); 393 | 394 | // get all but the left column from matrix 395 | var getAllButLeftCol$1 = doColumnOperation(getAllButTopRow$1); 396 | 397 | // get right column from matrix 398 | var getRightCol$1 = doColumnOperation(getBottomRow$1); 399 | 400 | // get all but right column from matrix 401 | var getAllButRightCol$1 = doColumnOperation(getAllButBottomRow$1); 402 | 403 | // get middle rows from matrix 404 | var getMiddleCols$1 = doColumnOperation(getMiddleRows$1); 405 | 406 | // Given a height and width, it returns the 407 | // length of the kth diagonal 408 | var lengthOfDiagonalFcn = function lengthOfDiagonalFcn(hgt, wid) { 409 | return function (k) { 410 | return Math.min(hgt, wid, wid - k, hgt + k); 411 | }; 412 | }; 413 | 414 | // return minor diagonals as an nArray 415 | var getMinorDiagonals$1 = function getMinorDiagonals(matrix) { 416 | // get height and width of matrix 417 | var _getMatrixDimensions = getMatrixDimensions$1(matrix), 418 | width = _getMatrixDimensions.width, 419 | height = _getMatrixDimensions.height; 420 | 421 | // set up function to determine the length of a diagonal 422 | 423 | 424 | var getDiagonalLength = lengthOfDiagonalFcn(height, width); 425 | 426 | // start at the top-left diagonal--this has index 1 - height (a negative number), 427 | // the program increments until it hits width - 1 428 | var startIndex = 1 - height; 429 | var stopIndex = width - 1; 430 | 431 | // define a recursive function that will extract the diagonals 432 | var getDiagonals = function getDiagonals(nArray, index) { 433 | // get the length of the current diagonal 434 | var lengthOfDiagonal = getDiagonalLength(index); 435 | 436 | // the values on the diagonals have been shifted so they're 437 | // at the beginning of the top rows... get the top rows 438 | var topRows = nArray.slice(0, lengthOfDiagonal); 439 | 440 | // get the first values of the top rows 441 | // note: need to reverse to preserve left-to-right order 442 | var heads = topRows.map(function (row) { 443 | return row[0]; 444 | }).reverse(); 445 | 446 | // terminate if index reaches width - 1 447 | if (index === stopIndex) { 448 | // return value in bottom-right corner of original matrix 449 | return [heads]; 450 | } else { 451 | // get tails of top rows 452 | var topTails = topRows.map(function (row) { 453 | return row.slice(1); 454 | }); 455 | // get rows that we didn't extract from 456 | var botRows = nArray.slice(lengthOfDiagonal); 457 | // join tails and bottom rows 458 | // filter out empty arrays [] 459 | var newMatrix = filterOutEmptyArrays([].concat(toConsumableArray(topTails), toConsumableArray(botRows))); 460 | 461 | // recursive step: call getDiagonals on reduced matrix, and 462 | // join with heads 463 | return [heads].concat(toConsumableArray(getDiagonals(newMatrix, index + 1))); 464 | } 465 | }; 466 | 467 | // call getDiagonals from the starting index 468 | return getDiagonals(matrix, startIndex); 469 | }; 470 | 471 | // Get major diagonals 472 | // flip matrix on top row, then get the minor diagonals 473 | var getMajorDiagonals$1 = function getMajorDiagonals(matrix) { 474 | return getMinorDiagonals$1(flipRows$1(matrix)); 475 | }; 476 | 477 | var formatOuterSpiral = function formatOuterSpiral(top, right, bottom, left) { 478 | var edgeArray = [].concat(toConsumableArray(top), toConsumableArray(right), [flattenArray(bottom).reverse(), flattenArray(left).reverse()]); 479 | 480 | return flattenArray(edgeArray).filter(function (d) { 481 | return d !== undefined; 482 | }); 483 | }; 484 | 485 | var getClockwiseSpiral$1 = function getClockwiseSpiral(matrix) { 486 | var _getMatrixDimensions = getMatrixDimensions$1(matrix), 487 | width = _getMatrixDimensions.width, 488 | height = _getMatrixDimensions.height; 489 | 490 | // terminal step if matrix is a row/column vector 491 | 492 | 493 | if (width < 2 || height < 2) { 494 | if (width === 0) { 495 | return []; 496 | } else { 497 | var termArr = height > width ? [].concat(toConsumableArray(getLeftCol$1(matrix))) : [].concat(toConsumableArray(getTopRow$1(matrix))); 498 | return flattenArray(termArr); 499 | } 500 | } else { 501 | // get top row 502 | var topRow = getTopRow$1(matrix); 503 | // get bottom row 504 | var botRow = getBottomRow$1(matrix); 505 | // get everything but top and bottom rows 506 | var middleMatrix = getMiddleRows$1(matrix); 507 | 508 | // get left column (minus top/bottom entries) 509 | var leftCol = getLeftCol$1(middleMatrix); 510 | // get right column (minus top/bottom entries) 511 | var rightCol = getRightCol$1(middleMatrix); 512 | 513 | // combine top, right, bottom, and left into an array 514 | var edgeArr = formatOuterSpiral(topRow, rightCol, botRow, leftCol); 515 | 516 | // get everything you didn't use 517 | var newMtx = getMiddleCols$1(middleMatrix); 518 | 519 | // append spiral array, then call again on the middle matrix 520 | var spiralArray = [edgeArr].concat(toConsumableArray(getClockwiseSpiral(newMtx))); 521 | 522 | // flatten the array once more 523 | return flattenArray(spiralArray); 524 | } 525 | }; 526 | 527 | var getCounterClockwiseSpiral$1 = function getCounterClockwiseSpiral(matrix) { 528 | return getClockwiseSpiral$1(transpose$1(matrix)); 529 | }; 530 | 531 | var wrp = matrixWrapper; 532 | 533 | var transpose$$1 = wrp(transpose$1); 534 | var applyToEntries$$1 = wrp(applyToEntries$1); 535 | 536 | var getMatrixWidth$$1 = wrp(getMatrixWidth$1); 537 | var getMatrixHeight$$1 = wrp(getMatrixHeight$1); 538 | var getMatrixDimensions$$1 = wrp(getMatrixDimensions$1); 539 | var sliceMatrixRows$$1 = wrp(sliceMatrixRows$1); 540 | var getTopRow$$1 = wrp(getTopRow$1); 541 | var getAllButTopRow$$1 = wrp(getAllButTopRow$1); 542 | var getBottomRow$$1 = wrp(getBottomRow$1); 543 | var getAllButBottomRow$$1 = wrp(getAllButBottomRow$1); 544 | var getMiddleRows$$1 = wrp(getMiddleRows$1); 545 | var sliceMatrixCols$$1 = wrp(sliceMatrixCols$1); 546 | var getLeftCol$$1 = wrp(getLeftCol$1); 547 | var getAllButLeftCol$$1 = wrp(getAllButLeftCol$1); 548 | var getRightCol$$1 = wrp(getAllButRightCol$1); 549 | var getAllButRightCol$$1 = wrp(getAllButRightCol$1); 550 | var getMiddleCols$$1 = wrp(getMiddleCols$1); 551 | 552 | var flipRows$$1 = wrp(flipRows$1); 553 | var flipCols$$1 = wrp(flipCols$1); 554 | 555 | var getMinorDiagonals$$1 = wrp(getMinorDiagonals$1); 556 | var getMajorDiagonals$$1 = wrp(getMajorDiagonals$1); 557 | 558 | var getClockwiseSpiral$$1 = wrp(getClockwiseSpiral$1); 559 | var getCounterClockwiseSpiral$$1 = wrp(getCounterClockwiseSpiral$1); 560 | 561 | exports.doMatrixCheck = doMatrixCheck; 562 | exports.matrixWrapper = matrixWrapper; 563 | exports.doColumnOperation = doColumnOperation; 564 | exports.transpose = transpose$$1; 565 | exports.applyToEntries = applyToEntries$$1; 566 | exports.getMatrixWidth = getMatrixWidth$$1; 567 | exports.getMatrixHeight = getMatrixHeight$$1; 568 | exports.getMatrixDimensions = getMatrixDimensions$$1; 569 | exports.sliceMatrixRows = sliceMatrixRows$$1; 570 | exports.getTopRow = getTopRow$$1; 571 | exports.getAllButTopRow = getAllButTopRow$$1; 572 | exports.getBottomRow = getBottomRow$$1; 573 | exports.getAllButBottomRow = getAllButBottomRow$$1; 574 | exports.getMiddleRows = getMiddleRows$$1; 575 | exports.sliceMatrixCols = sliceMatrixCols$$1; 576 | exports.getLeftCol = getLeftCol$$1; 577 | exports.getAllButLeftCol = getAllButLeftCol$$1; 578 | exports.getRightCol = getRightCol$$1; 579 | exports.getAllButRightCol = getAllButRightCol$$1; 580 | exports.getMiddleCols = getMiddleCols$$1; 581 | exports.flipRows = flipRows$$1; 582 | exports.flipCols = flipCols$$1; 583 | exports.getMinorDiagonals = getMinorDiagonals$$1; 584 | exports.getMajorDiagonals = getMajorDiagonals$$1; 585 | exports.getClockwiseSpiral = getClockwiseSpiral$$1; 586 | exports.getCounterClockwiseSpiral = getCounterClockwiseSpiral$$1; 587 | exports.getFirstElement = getFirstElement; 588 | exports.getAllButFirstElement = getAllButFirstElement; 589 | exports.getLastElement = getLastElement; 590 | exports.getAllButLastElement = getAllButLastElement; 591 | exports.getMiddleElements = getMiddleElements; 592 | exports.filterOutEmptyArrays = filterOutEmptyArrays; 593 | exports.flattenArray = flattenArray; 594 | -------------------------------------------------------------------------------- /build/index.es.js: -------------------------------------------------------------------------------- 1 | // Gets first element in array 2 | var getFirstElement = function getFirstElement(arr) { 3 | return arr[0]; 4 | }; 5 | 6 | // Gets all but first element in array 7 | var getAllButFirstElement = function getAllButFirstElement(arr) { 8 | return arr.slice(1); 9 | }; 10 | 11 | // Gets last element in array 12 | var getLastElement = function getLastElement(arr) { 13 | return arr[arr.length - 1]; 14 | }; 15 | 16 | // Gets all but the last element in array 17 | var getAllButLastElement = function getAllButLastElement(arr) { 18 | return arr.slice(0, arr.length - 1); 19 | }; 20 | 21 | // Gets middle elements in array 22 | var getMiddleElements = function getMiddleElements(arr) { 23 | return arr.slice(1, arr.length - 1); 24 | }; 25 | 26 | // Given an nArray, it filters out empty arrays 27 | var filterOutEmptyArrays = function filterOutEmptyArrays(nArray) { 28 | return nArray.filter(function (arr) { 29 | return arr.length > 0; 30 | }); 31 | }; 32 | 33 | var flattenArray = function flattenArray(nArray) { 34 | return nArray.reduce(function (acc, cur) { 35 | return acc.concat(cur); 36 | }, []); 37 | }; 38 | 39 | // Get height of matrix 40 | var getMatrixHeight$1 = function getMatrixHeight(mtx) { 41 | return mtx.length; 42 | }; 43 | 44 | // Get width of matrix 45 | var getMatrixWidth$1 = function getMatrixWidth(mtx) { 46 | return mtx.length > 0 ? mtx[0].length : 0; 47 | }; 48 | 49 | // Get matrix width and height 50 | // return {width, height} object 51 | var getMatrixDimensions$1 = function getMatrixDimensions(mtx) { 52 | var height = getMatrixHeight$1(mtx); 53 | var width = getMatrixWidth$1(mtx); 54 | 55 | return { width: width, height: height }; 56 | }; 57 | 58 | var asyncGenerator = function () { 59 | function AwaitValue(value) { 60 | this.value = value; 61 | } 62 | 63 | function AsyncGenerator(gen) { 64 | var front, back; 65 | 66 | function send(key, arg) { 67 | return new Promise(function (resolve, reject) { 68 | var request = { 69 | key: key, 70 | arg: arg, 71 | resolve: resolve, 72 | reject: reject, 73 | next: null 74 | }; 75 | 76 | if (back) { 77 | back = back.next = request; 78 | } else { 79 | front = back = request; 80 | resume(key, arg); 81 | } 82 | }); 83 | } 84 | 85 | function resume(key, arg) { 86 | try { 87 | var result = gen[key](arg); 88 | var value = result.value; 89 | 90 | if (value instanceof AwaitValue) { 91 | Promise.resolve(value.value).then(function (arg) { 92 | resume("next", arg); 93 | }, function (arg) { 94 | resume("throw", arg); 95 | }); 96 | } else { 97 | settle(result.done ? "return" : "normal", result.value); 98 | } 99 | } catch (err) { 100 | settle("throw", err); 101 | } 102 | } 103 | 104 | function settle(type, value) { 105 | switch (type) { 106 | case "return": 107 | front.resolve({ 108 | value: value, 109 | done: true 110 | }); 111 | break; 112 | 113 | case "throw": 114 | front.reject(value); 115 | break; 116 | 117 | default: 118 | front.resolve({ 119 | value: value, 120 | done: false 121 | }); 122 | break; 123 | } 124 | 125 | front = front.next; 126 | 127 | if (front) { 128 | resume(front.key, front.arg); 129 | } else { 130 | back = null; 131 | } 132 | } 133 | 134 | this._invoke = send; 135 | 136 | if (typeof gen.return !== "function") { 137 | this.return = undefined; 138 | } 139 | } 140 | 141 | if (typeof Symbol === "function" && Symbol.asyncIterator) { 142 | AsyncGenerator.prototype[Symbol.asyncIterator] = function () { 143 | return this; 144 | }; 145 | } 146 | 147 | AsyncGenerator.prototype.next = function (arg) { 148 | return this._invoke("next", arg); 149 | }; 150 | 151 | AsyncGenerator.prototype.throw = function (arg) { 152 | return this._invoke("throw", arg); 153 | }; 154 | 155 | AsyncGenerator.prototype.return = function (arg) { 156 | return this._invoke("return", arg); 157 | }; 158 | 159 | return { 160 | wrap: function (fn) { 161 | return function () { 162 | return new AsyncGenerator(fn.apply(this, arguments)); 163 | }; 164 | }, 165 | await: function (value) { 166 | return new AwaitValue(value); 167 | } 168 | }; 169 | }(); 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | var toConsumableArray = function (arr) { 220 | if (Array.isArray(arr)) { 221 | for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; 222 | 223 | return arr2; 224 | } else { 225 | return Array.from(arr); 226 | } 227 | }; 228 | 229 | // Transpose a matrix 230 | var transpose$1 = function transpose(mtx) { 231 | var width = getMatrixWidth$1(mtx); 232 | 233 | var leftCol = mtx.map(getFirstElement); 234 | 235 | if (width < 2) { 236 | return [leftCol]; 237 | } else { 238 | var rightCols = mtx.map(getAllButFirstElement); 239 | return [leftCol].concat(toConsumableArray(transpose(rightCols))); 240 | } 241 | }; 242 | 243 | // the empty matrix 244 | var emptyMatrix = [[]]; 245 | 246 | // check if matrix is empty 247 | var checkIfEmptyMatrix = function checkIfEmptyMatrix(mtx) { 248 | var isAnArray = Array.isArray(mtx); 249 | 250 | return !isAnArray ? false : mtx.length === 1 ? mtx[0].length === 0 : false; 251 | }; 252 | 253 | // boolean product, passed to a reducer 254 | var booleanProduct = function booleanProduct(acc, curr) { 255 | return Boolean(acc * curr); 256 | }; 257 | 258 | // check if nArray is a matrix 259 | var checkIfMatrix = function checkIfMatrix(nArray) { 260 | if (nArray.length === 0) { 261 | return true; 262 | } 263 | 264 | var checkIfArrays = nArray.map(function (d) { 265 | return Array.isArray(d); 266 | }).reduce(booleanProduct, true); 267 | 268 | if (checkIfArrays === false) { 269 | return false; 270 | } 271 | 272 | var firstRow = nArray[0]; 273 | var height = nArray.length; 274 | var width = firstRow.length; 275 | 276 | if (height < 2) { 277 | return true; 278 | } else { 279 | return nArray.map(function (d) { 280 | return d.length === width; 281 | }).reduce(booleanProduct, true); 282 | } 283 | }; 284 | 285 | // decorator to check if nArray is a matrix 286 | var doMatrixCheck = function doMatrixCheck(fcn) { 287 | return function (nArray) { 288 | for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 289 | args[_key - 1] = arguments[_key]; 290 | } 291 | 292 | if (checkIfMatrix(nArray) === false) { 293 | console.error("not a matrix"); 294 | throw new Error(); 295 | } else { 296 | return fcn.apply(undefined, [nArray].concat(args)); 297 | } 298 | }; 299 | }; 300 | 301 | // A decorator for any matrix operation: 302 | // * check if it's the identity 303 | // * check if it's a matrix 304 | // * apply a function 305 | var matrixWrapper = function matrixWrapper(fcn) { 306 | return function (nArray) { 307 | for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { 308 | args[_key2 - 1] = arguments[_key2]; 309 | } 310 | 311 | if (checkIfEmptyMatrix(nArray)) { 312 | return emptyMatrix; 313 | } else { 314 | return doMatrixCheck(fcn).apply(undefined, [nArray].concat(args)); 315 | } 316 | }; 317 | }; 318 | 319 | // transform matrix to column space, do operation, transpose back 320 | var doColumnOperation = function doColumnOperation(fcn) { 321 | return function (mtx) { 322 | for (var _len3 = arguments.length, args = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { 323 | args[_key3 - 1] = arguments[_key3]; 324 | } 325 | 326 | return transpose$1(fcn.apply(undefined, [transpose$1(mtx)].concat(args))); 327 | }; 328 | }; 329 | 330 | var applyToEntries$1 = function applyToEntries(mtx) { 331 | for (var _len4 = arguments.length, args = Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) { 332 | args[_key4 - 1] = arguments[_key4]; 333 | } 334 | 335 | return function (fcn) { 336 | return mtx.map(function (r) { 337 | return r.map(function (entry) { 338 | return fcn.apply(undefined, [entry].concat(args)); 339 | }); 340 | }); 341 | }; 342 | }; 343 | 344 | var flipRows$1 = function flipRows(mtx) { 345 | return mtx.slice().reverse(); 346 | }; 347 | 348 | var flipCols$1 = doColumnOperation(flipRows$1); 349 | 350 | // Slice rows from matrix 351 | var sliceMatrixRows$1 = function sliceMatrixRows(mtx, startIndex, stopIndex) { 352 | if (stopIndex === undefined) { 353 | return mtx.slice(startIndex); 354 | } else { 355 | return mtx.slice(startIndex, stopIndex); 356 | } 357 | }; 358 | 359 | // Get top row from matrix 360 | var getTopRow$1 = function getTopRow(mtx) { 361 | return [getFirstElement(mtx)]; 362 | }; 363 | 364 | // Get all but top row from matrix 365 | var getAllButTopRow$1 = function getAllButTopRow(mtx) { 366 | return getAllButFirstElement(mtx); 367 | }; 368 | 369 | // Get bottom row from matrix 370 | var getBottomRow$1 = function getBottomRow(mtx) { 371 | return [getLastElement(mtx)]; 372 | }; 373 | 374 | // Get all but bottom row from matrix 375 | var getAllButBottomRow$1 = function getAllButBottomRow(mtx) { 376 | return getAllButLastElement(mtx); 377 | }; 378 | 379 | // Get middle rows from matrix 380 | var getMiddleRows$1 = function getMiddleRows(mtx) { 381 | return getMiddleElements(mtx); 382 | }; 383 | 384 | // Slice columns from matrix 385 | var sliceMatrixCols$1 = doColumnOperation(sliceMatrixRows$1); 386 | 387 | // get left column from matrix 388 | var getLeftCol$1 = doColumnOperation(getTopRow$1); 389 | 390 | // get all but the left column from matrix 391 | var getAllButLeftCol$1 = doColumnOperation(getAllButTopRow$1); 392 | 393 | // get right column from matrix 394 | var getRightCol$1 = doColumnOperation(getBottomRow$1); 395 | 396 | // get all but right column from matrix 397 | var getAllButRightCol$1 = doColumnOperation(getAllButBottomRow$1); 398 | 399 | // get middle rows from matrix 400 | var getMiddleCols$1 = doColumnOperation(getMiddleRows$1); 401 | 402 | // Given a height and width, it returns the 403 | // length of the kth diagonal 404 | var lengthOfDiagonalFcn = function lengthOfDiagonalFcn(hgt, wid) { 405 | return function (k) { 406 | return Math.min(hgt, wid, wid - k, hgt + k); 407 | }; 408 | }; 409 | 410 | // return minor diagonals as an nArray 411 | var getMinorDiagonals$1 = function getMinorDiagonals(matrix) { 412 | // get height and width of matrix 413 | var _getMatrixDimensions = getMatrixDimensions$1(matrix), 414 | width = _getMatrixDimensions.width, 415 | height = _getMatrixDimensions.height; 416 | 417 | // set up function to determine the length of a diagonal 418 | 419 | 420 | var getDiagonalLength = lengthOfDiagonalFcn(height, width); 421 | 422 | // start at the top-left diagonal--this has index 1 - height (a negative number), 423 | // the program increments until it hits width - 1 424 | var startIndex = 1 - height; 425 | var stopIndex = width - 1; 426 | 427 | // define a recursive function that will extract the diagonals 428 | var getDiagonals = function getDiagonals(nArray, index) { 429 | // get the length of the current diagonal 430 | var lengthOfDiagonal = getDiagonalLength(index); 431 | 432 | // the values on the diagonals have been shifted so they're 433 | // at the beginning of the top rows... get the top rows 434 | var topRows = nArray.slice(0, lengthOfDiagonal); 435 | 436 | // get the first values of the top rows 437 | // note: need to reverse to preserve left-to-right order 438 | var heads = topRows.map(function (row) { 439 | return row[0]; 440 | }).reverse(); 441 | 442 | // terminate if index reaches width - 1 443 | if (index === stopIndex) { 444 | // return value in bottom-right corner of original matrix 445 | return [heads]; 446 | } else { 447 | // get tails of top rows 448 | var topTails = topRows.map(function (row) { 449 | return row.slice(1); 450 | }); 451 | // get rows that we didn't extract from 452 | var botRows = nArray.slice(lengthOfDiagonal); 453 | // join tails and bottom rows 454 | // filter out empty arrays [] 455 | var newMatrix = filterOutEmptyArrays([].concat(toConsumableArray(topTails), toConsumableArray(botRows))); 456 | 457 | // recursive step: call getDiagonals on reduced matrix, and 458 | // join with heads 459 | return [heads].concat(toConsumableArray(getDiagonals(newMatrix, index + 1))); 460 | } 461 | }; 462 | 463 | // call getDiagonals from the starting index 464 | return getDiagonals(matrix, startIndex); 465 | }; 466 | 467 | // Get major diagonals 468 | // flip matrix on top row, then get the minor diagonals 469 | var getMajorDiagonals$1 = function getMajorDiagonals(matrix) { 470 | return getMinorDiagonals$1(flipRows$1(matrix)); 471 | }; 472 | 473 | var formatOuterSpiral = function formatOuterSpiral(top, right, bottom, left) { 474 | var edgeArray = [].concat(toConsumableArray(top), toConsumableArray(right), [flattenArray(bottom).reverse(), flattenArray(left).reverse()]); 475 | 476 | return flattenArray(edgeArray).filter(function (d) { 477 | return d !== undefined; 478 | }); 479 | }; 480 | 481 | var getClockwiseSpiral$1 = function getClockwiseSpiral(matrix) { 482 | var _getMatrixDimensions = getMatrixDimensions$1(matrix), 483 | width = _getMatrixDimensions.width, 484 | height = _getMatrixDimensions.height; 485 | 486 | // terminal step if matrix is a row/column vector 487 | 488 | 489 | if (width < 2 || height < 2) { 490 | if (width === 0) { 491 | return []; 492 | } else { 493 | var termArr = height > width ? [].concat(toConsumableArray(getLeftCol$1(matrix))) : [].concat(toConsumableArray(getTopRow$1(matrix))); 494 | return flattenArray(termArr); 495 | } 496 | } else { 497 | // get top row 498 | var topRow = getTopRow$1(matrix); 499 | // get bottom row 500 | var botRow = getBottomRow$1(matrix); 501 | // get everything but top and bottom rows 502 | var middleMatrix = getMiddleRows$1(matrix); 503 | 504 | // get left column (minus top/bottom entries) 505 | var leftCol = getLeftCol$1(middleMatrix); 506 | // get right column (minus top/bottom entries) 507 | var rightCol = getRightCol$1(middleMatrix); 508 | 509 | // combine top, right, bottom, and left into an array 510 | var edgeArr = formatOuterSpiral(topRow, rightCol, botRow, leftCol); 511 | 512 | // get everything you didn't use 513 | var newMtx = getMiddleCols$1(middleMatrix); 514 | 515 | // append spiral array, then call again on the middle matrix 516 | var spiralArray = [edgeArr].concat(toConsumableArray(getClockwiseSpiral(newMtx))); 517 | 518 | // flatten the array once more 519 | return flattenArray(spiralArray); 520 | } 521 | }; 522 | 523 | var getCounterClockwiseSpiral$1 = function getCounterClockwiseSpiral(matrix) { 524 | return getClockwiseSpiral$1(transpose$1(matrix)); 525 | }; 526 | 527 | var wrp = matrixWrapper; 528 | 529 | var transpose$$1 = wrp(transpose$1); 530 | var applyToEntries$$1 = wrp(applyToEntries$1); 531 | 532 | var getMatrixWidth$$1 = wrp(getMatrixWidth$1); 533 | var getMatrixHeight$$1 = wrp(getMatrixHeight$1); 534 | var getMatrixDimensions$$1 = wrp(getMatrixDimensions$1); 535 | var sliceMatrixRows$$1 = wrp(sliceMatrixRows$1); 536 | var getTopRow$$1 = wrp(getTopRow$1); 537 | var getAllButTopRow$$1 = wrp(getAllButTopRow$1); 538 | var getBottomRow$$1 = wrp(getBottomRow$1); 539 | var getAllButBottomRow$$1 = wrp(getAllButBottomRow$1); 540 | var getMiddleRows$$1 = wrp(getMiddleRows$1); 541 | var sliceMatrixCols$$1 = wrp(sliceMatrixCols$1); 542 | var getLeftCol$$1 = wrp(getLeftCol$1); 543 | var getAllButLeftCol$$1 = wrp(getAllButLeftCol$1); 544 | var getRightCol$$1 = wrp(getAllButRightCol$1); 545 | var getAllButRightCol$$1 = wrp(getAllButRightCol$1); 546 | var getMiddleCols$$1 = wrp(getMiddleCols$1); 547 | 548 | var flipRows$$1 = wrp(flipRows$1); 549 | var flipCols$$1 = wrp(flipCols$1); 550 | 551 | var getMinorDiagonals$$1 = wrp(getMinorDiagonals$1); 552 | var getMajorDiagonals$$1 = wrp(getMajorDiagonals$1); 553 | 554 | var getClockwiseSpiral$$1 = wrp(getClockwiseSpiral$1); 555 | var getCounterClockwiseSpiral$$1 = wrp(getCounterClockwiseSpiral$1); 556 | 557 | export { doMatrixCheck, matrixWrapper, doColumnOperation, transpose$$1 as transpose, applyToEntries$$1 as applyToEntries, getMatrixWidth$$1 as getMatrixWidth, getMatrixHeight$$1 as getMatrixHeight, getMatrixDimensions$$1 as getMatrixDimensions, sliceMatrixRows$$1 as sliceMatrixRows, getTopRow$$1 as getTopRow, getAllButTopRow$$1 as getAllButTopRow, getBottomRow$$1 as getBottomRow, getAllButBottomRow$$1 as getAllButBottomRow, getMiddleRows$$1 as getMiddleRows, sliceMatrixCols$$1 as sliceMatrixCols, getLeftCol$$1 as getLeftCol, getAllButLeftCol$$1 as getAllButLeftCol, getRightCol$$1 as getRightCol, getAllButRightCol$$1 as getAllButRightCol, getMiddleCols$$1 as getMiddleCols, flipRows$$1 as flipRows, flipCols$$1 as flipCols, getMinorDiagonals$$1 as getMinorDiagonals, getMajorDiagonals$$1 as getMajorDiagonals, getClockwiseSpiral$$1 as getClockwiseSpiral, getCounterClockwiseSpiral$$1 as getCounterClockwiseSpiral, getFirstElement, getAllButFirstElement, getLastElement, getAllButLastElement, getMiddleElements, filterOutEmptyArrays, flattenArray }; 558 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "matrix-magic", 3 | "version": "1.1.3", 4 | "description": "library for manipulating matrices", 5 | "main": "build/index.cjs.js", 6 | "module": "build/index.es.js", 7 | "scripts": { 8 | "dev": "watch 'npm run build' src", 9 | "build": "rollup --config", 10 | "test": "jest", 11 | "test:watch": "npm test -- --watch", 12 | "coverage": "jest --coverage" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/trainorpj/matrix-magic" 17 | }, 18 | "keywords": [ 19 | "matrix", 20 | "fp", 21 | "recursion" 22 | ], 23 | "author": "PJ Trainor", 24 | "homepage": "https://matrix-magic.now.sh/", 25 | "license": "MIT", 26 | "devDependencies": { 27 | "babel-cli": "^6.26.0", 28 | "babel-plugin-external-helpers": "^6.22.0", 29 | "babel-preset-env": "^1.6.1", 30 | "jest": "^20.0.4", 31 | "rollup-plugin-babel": "^3.0.2", 32 | "rollup-plugin-node-resolve": "^3.0.0", 33 | "watch": "^1.0.2" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from "rollup-plugin-node-resolve"; 2 | import babel from "rollup-plugin-babel"; 3 | 4 | export default { 5 | input: "src/index.js", 6 | output: [ 7 | { 8 | file: "build/index.cjs.js", 9 | format: "cjs" 10 | }, 11 | { 12 | file: "build/index.es.js", 13 | format: "es" 14 | } 15 | ], 16 | plugins: [ 17 | resolve(), 18 | babel({ 19 | presets: [ 20 | [ 21 | "env", 22 | { 23 | modules: false 24 | } 25 | ] 26 | ], 27 | plugins: ["external-helpers"], 28 | babelrc: false 29 | }) 30 | ] 31 | }; 32 | -------------------------------------------------------------------------------- /src/arrayUtils.js: -------------------------------------------------------------------------------- 1 | // Gets first element in array 2 | export const getFirstElement = arr => arr[0]; 3 | 4 | // Gets all but first element in array 5 | export const getAllButFirstElement = arr => arr.slice(1); 6 | 7 | // Gets last element in array 8 | export const getLastElement = arr => arr[arr.length - 1]; 9 | 10 | // Gets all but the last element in array 11 | export const getAllButLastElement = arr => arr.slice(0, arr.length - 1); 12 | 13 | // Gets middle elements in array 14 | export const getMiddleElements = arr => arr.slice(1, arr.length - 1); 15 | 16 | // Given an nArray, it filters out empty arrays 17 | export const filterOutEmptyArrays = nArray => 18 | nArray.filter(arr => arr.length > 0); 19 | 20 | export const flattenArray = nArray => 21 | nArray.reduce((acc, cur) => acc.concat(cur), []); 22 | -------------------------------------------------------------------------------- /src/getDiagonals.js: -------------------------------------------------------------------------------- 1 | import { getMatrixDimensions } from "./matrixDimensions"; 2 | import { doColumnOperation } from "./matrixDecorators"; 3 | import { flipRows } from "./matrixShift"; 4 | import { filterOutEmptyArrays } from "./arrayUtils"; 5 | 6 | // Given a height and width, it returns the 7 | // length of the kth diagonal 8 | const lengthOfDiagonalFcn = (hgt, wid) => k => 9 | Math.min(hgt, wid, wid - k, hgt + k); 10 | 11 | // return minor diagonals as an nArray 12 | export const getMinorDiagonals = matrix => { 13 | // get height and width of matrix 14 | const { width, height } = getMatrixDimensions(matrix); 15 | 16 | // set up function to determine the length of a diagonal 17 | const getDiagonalLength = lengthOfDiagonalFcn(height, width); 18 | 19 | // start at the top-left diagonal--this has index 1 - height (a negative number), 20 | // the program increments until it hits width - 1 21 | const startIndex = 1 - height; 22 | const stopIndex = width - 1; 23 | 24 | // define a recursive function that will extract the diagonals 25 | const getDiagonals = (nArray, index) => { 26 | // get the length of the current diagonal 27 | const lengthOfDiagonal = getDiagonalLength(index); 28 | 29 | // the values on the diagonals have been shifted so they're 30 | // at the beginning of the top rows... get the top rows 31 | const topRows = nArray.slice(0, lengthOfDiagonal); 32 | 33 | // get the first values of the top rows 34 | // note: need to reverse to preserve left-to-right order 35 | const heads = topRows.map(row => row[0]).reverse(); 36 | 37 | // terminate if index reaches width - 1 38 | if (index === stopIndex) { 39 | // return value in bottom-right corner of original matrix 40 | return [heads]; 41 | } else { 42 | // get tails of top rows 43 | const topTails = topRows.map(row => row.slice(1)); 44 | // get rows that we didn't extract from 45 | const botRows = nArray.slice(lengthOfDiagonal); 46 | // join tails and bottom rows 47 | // filter out empty arrays [] 48 | const newMatrix = filterOutEmptyArrays([...topTails, ...botRows]); 49 | 50 | // recursive step: call getDiagonals on reduced matrix, and 51 | // join with heads 52 | return [heads, ...getDiagonals(newMatrix, index + 1)]; 53 | } 54 | }; 55 | 56 | // call getDiagonals from the starting index 57 | return getDiagonals(matrix, startIndex); 58 | }; 59 | 60 | // Get major diagonals 61 | // flip matrix on top row, then get the minor diagonals 62 | export const getMajorDiagonals = matrix => getMinorDiagonals(flipRows(matrix)); 63 | -------------------------------------------------------------------------------- /src/getSpiral.js: -------------------------------------------------------------------------------- 1 | import { transpose } from "./transpose"; 2 | import { flattenArray } from "./arrayUtils"; 3 | import { getMatrixDimensions } from "./matrixDimensions"; 4 | import { 5 | getLeftCol, 6 | getTopRow, 7 | getBottomRow, 8 | getMiddleRows, 9 | getRightCol, 10 | getMiddleCols 11 | } from "./matrixSlice"; 12 | 13 | const formatOuterSpiral = (top, right, bottom, left) => { 14 | const edgeArray = [ 15 | ...top, 16 | ...right, 17 | flattenArray(bottom).reverse(), 18 | flattenArray(left).reverse() 19 | ]; 20 | 21 | return flattenArray(edgeArray).filter(d => d !== undefined); 22 | }; 23 | 24 | export const getClockwiseSpiral = matrix => { 25 | const { width, height } = getMatrixDimensions(matrix); 26 | 27 | // terminal step if matrix is a row/column vector 28 | if (width < 2 || height < 2) { 29 | if (width === 0) { 30 | return []; 31 | } else { 32 | const termArr = 33 | height > width ? [...getLeftCol(matrix)] : [...getTopRow(matrix)]; 34 | return flattenArray(termArr); 35 | } 36 | } else { 37 | // get top row 38 | const topRow = getTopRow(matrix); 39 | // get bottom row 40 | const botRow = getBottomRow(matrix); 41 | // get everything but top and bottom rows 42 | const middleMatrix = getMiddleRows(matrix); 43 | 44 | // get left column (minus top/bottom entries) 45 | const leftCol = getLeftCol(middleMatrix); 46 | // get right column (minus top/bottom entries) 47 | const rightCol = getRightCol(middleMatrix); 48 | 49 | // combine top, right, bottom, and left into an array 50 | const edgeArr = formatOuterSpiral(topRow, rightCol, botRow, leftCol); 51 | 52 | // get everything you didn't use 53 | const newMtx = getMiddleCols(middleMatrix); 54 | 55 | // append spiral array, then call again on the middle matrix 56 | const spiralArray = [edgeArr, ...getClockwiseSpiral(newMtx)]; 57 | 58 | // flatten the array once more 59 | return flattenArray(spiralArray); 60 | } 61 | }; 62 | 63 | export const getCounterClockwiseSpiral = matrix => 64 | getClockwiseSpiral(transpose(matrix)); 65 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export { 2 | getFirstElement, 3 | getAllButFirstElement, 4 | getLastElement, 5 | getAllButLastElement, 6 | getMiddleElements, 7 | filterOutEmptyArrays, 8 | flattenArray 9 | } from "./arrayUtils"; 10 | 11 | import { transpose as transpose_ } from "./transpose"; 12 | 13 | import { 14 | doMatrixCheck, 15 | matrixWrapper, 16 | doColumnOperation 17 | } from "./matrixDecorators"; 18 | 19 | export { doMatrixCheck, matrixWrapper, doColumnOperation }; 20 | 21 | import { applyToEntries as applyToEntries_ } from "./matrixDecorators"; 22 | 23 | import { 24 | getMatrixWidth as getMatrixWidth_, 25 | getMatrixHeight as getMatrixHeight_, 26 | getMatrixDimensions as getMatrixDimensions_ 27 | } from "./matrixDimensions"; 28 | 29 | import { flipRows as flipRows_, flipCols as flipCols_ } from "./matrixShift"; 30 | 31 | import { 32 | sliceMatrixRows as sliceMatrixRows_, 33 | getTopRow as getTopRow_, 34 | getAllButTopRow as getAllButTopRow_, 35 | getBottomRow as getBottomRow_, 36 | getAllButBottomRow as getAllButBottomRow_, 37 | getMiddleRows as getMiddleRows_, 38 | sliceMatrixCols as sliceMatrixCols_, 39 | getLeftCol as getLeftCol_, 40 | getAllButLeftCol as getAllButLeftCol_, 41 | getRightCol as getRightCol_, 42 | getAllButRightCol as getAllButRightCol_, 43 | getMiddleCols as getMiddleCols_ 44 | } from "./matrixSlice"; 45 | 46 | import { 47 | getMinorDiagonals as getMinorDiagonals_, 48 | getMajorDiagonals as getMajorDiagonals_ 49 | } from "./getDiagonals"; 50 | 51 | import { 52 | getClockwiseSpiral as getClockwiseSpiral_, 53 | getCounterClockwiseSpiral as getCounterClockwiseSpiral_ 54 | } from "./getSpiral"; 55 | 56 | const wrp = matrixWrapper; 57 | 58 | export const transpose = wrp(transpose_); 59 | export const applyToEntries = wrp(applyToEntries_); 60 | 61 | export const getMatrixWidth = wrp(getMatrixWidth_); 62 | export const getMatrixHeight = wrp(getMatrixHeight_); 63 | export const getMatrixDimensions = wrp(getMatrixDimensions_); 64 | export const sliceMatrixRows = wrp(sliceMatrixRows_); 65 | export const getTopRow = wrp(getTopRow_); 66 | export const getAllButTopRow = wrp(getAllButTopRow_); 67 | export const getBottomRow = wrp(getBottomRow_); 68 | export const getAllButBottomRow = wrp(getAllButBottomRow_); 69 | export const getMiddleRows = wrp(getMiddleRows_); 70 | export const sliceMatrixCols = wrp(sliceMatrixCols_); 71 | export const getLeftCol = wrp(getLeftCol_); 72 | export const getAllButLeftCol = wrp(getAllButLeftCol_); 73 | export const getRightCol = wrp(getAllButRightCol_); 74 | export const getAllButRightCol = wrp(getAllButRightCol_); 75 | export const getMiddleCols = wrp(getMiddleCols_); 76 | 77 | export const flipRows = wrp(flipRows_); 78 | export const flipCols = wrp(flipCols_); 79 | 80 | export const getMinorDiagonals = wrp(getMinorDiagonals_); 81 | export const getMajorDiagonals = wrp(getMajorDiagonals_); 82 | 83 | export const getClockwiseSpiral = wrp(getClockwiseSpiral_); 84 | export const getCounterClockwiseSpiral = wrp(getCounterClockwiseSpiral_); 85 | -------------------------------------------------------------------------------- /src/matrixDecorators.js: -------------------------------------------------------------------------------- 1 | import { transpose } from "./transpose"; 2 | 3 | // the empty matrix 4 | const emptyMatrix = [[]]; 5 | 6 | // check if matrix is empty 7 | const checkIfEmptyMatrix = mtx => { 8 | const isAnArray = Array.isArray(mtx); 9 | 10 | return !isAnArray ? false : mtx.length === 1 ? mtx[0].length === 0 : false; 11 | }; 12 | 13 | // boolean product, passed to a reducer 14 | const booleanProduct = (acc, curr) => Boolean(acc * curr); 15 | 16 | // check if nArray is a matrix 17 | const checkIfMatrix = nArray => { 18 | if (nArray.length === 0) { 19 | return true; 20 | } 21 | 22 | const checkIfArrays = nArray 23 | .map(d => Array.isArray(d)) 24 | .reduce(booleanProduct, true); 25 | 26 | if (checkIfArrays === false) { 27 | return false; 28 | } 29 | 30 | const firstRow = nArray[0]; 31 | const height = nArray.length; 32 | const width = firstRow.length; 33 | 34 | if (height < 2) { 35 | return true; 36 | } else { 37 | return nArray.map(d => d.length === width).reduce(booleanProduct, true); 38 | } 39 | }; 40 | 41 | // decorator to check if nArray is a matrix 42 | export const doMatrixCheck = fcn => (nArray, ...args) => { 43 | if (checkIfMatrix(nArray) === false) { 44 | console.error("not a matrix"); 45 | throw new Error(); 46 | } else { 47 | return fcn(nArray, ...args); 48 | } 49 | }; 50 | 51 | // A decorator for any matrix operation: 52 | // * check if it's the identity 53 | // * check if it's a matrix 54 | // * apply a function 55 | export const matrixWrapper = fcn => (nArray, ...args) => { 56 | if (checkIfEmptyMatrix(nArray)) { 57 | return emptyMatrix; 58 | } else { 59 | return doMatrixCheck(fcn)(nArray, ...args); 60 | } 61 | }; 62 | 63 | // transform matrix to column space, do operation, transpose back 64 | export const doColumnOperation = fcn => (mtx, ...args) => { 65 | return transpose(fcn(transpose(mtx), ...args)); 66 | }; 67 | 68 | export const applyToEntries = (mtx, ...args) => fcn => { 69 | return mtx.map(r => { 70 | return r.map(entry => fcn(entry, ...args)); 71 | }); 72 | }; 73 | -------------------------------------------------------------------------------- /src/matrixDimensions.js: -------------------------------------------------------------------------------- 1 | // Get height of matrix 2 | export const getMatrixHeight = mtx => mtx.length; 3 | 4 | // Get width of matrix 5 | export const getMatrixWidth = mtx => (mtx.length > 0 ? mtx[0].length : 0); 6 | 7 | // Get matrix width and height 8 | // return {width, height} object 9 | export const getMatrixDimensions = mtx => { 10 | const height = getMatrixHeight(mtx); 11 | const width = getMatrixWidth(mtx); 12 | 13 | return { width, height }; 14 | }; 15 | -------------------------------------------------------------------------------- /src/matrixShift.js: -------------------------------------------------------------------------------- 1 | import { doColumnOperation } from "./matrixDecorators"; 2 | 3 | export const flipRows = mtx => mtx.slice().reverse(); 4 | 5 | export const flipCols = doColumnOperation(flipRows); 6 | -------------------------------------------------------------------------------- /src/matrixSlice.js: -------------------------------------------------------------------------------- 1 | import { 2 | getFirstElement, 3 | getLastElement, 4 | getAllButFirstElement, 5 | getAllButLastElement, 6 | getMiddleElements 7 | } from "./arrayUtils"; 8 | import { doColumnOperation } from "./matrixDecorators"; 9 | 10 | // Slice rows from matrix 11 | export const sliceMatrixRows = (mtx, startIndex, stopIndex) => { 12 | if (stopIndex === undefined) { 13 | return mtx.slice(startIndex); 14 | } else { 15 | return mtx.slice(startIndex, stopIndex); 16 | } 17 | }; 18 | 19 | // Get top row from matrix 20 | export const getTopRow = mtx => [getFirstElement(mtx)]; 21 | 22 | // Get all but top row from matrix 23 | export const getAllButTopRow = mtx => getAllButFirstElement(mtx); 24 | 25 | // Get bottom row from matrix 26 | export const getBottomRow = mtx => [getLastElement(mtx)]; 27 | 28 | // Get all but bottom row from matrix 29 | export const getAllButBottomRow = mtx => getAllButLastElement(mtx); 30 | 31 | // Get middle rows from matrix 32 | export const getMiddleRows = mtx => getMiddleElements(mtx); 33 | 34 | // Slice columns from matrix 35 | export const sliceMatrixCols = doColumnOperation(sliceMatrixRows); 36 | 37 | // get left column from matrix 38 | export const getLeftCol = doColumnOperation(getTopRow); 39 | 40 | // get all but the left column from matrix 41 | export const getAllButLeftCol = doColumnOperation(getAllButTopRow); 42 | 43 | // get right column from matrix 44 | export const getRightCol = doColumnOperation(getBottomRow); 45 | 46 | // get all but right column from matrix 47 | export const getAllButRightCol = doColumnOperation(getAllButBottomRow); 48 | 49 | // get middle rows from matrix 50 | export const getMiddleCols = doColumnOperation(getMiddleRows); 51 | -------------------------------------------------------------------------------- /src/transpose.js: -------------------------------------------------------------------------------- 1 | import { getFirstElement, getAllButFirstElement } from "./arrayUtils"; 2 | import { getMatrixWidth } from "./matrixDimensions"; 3 | 4 | // Transpose a matrix 5 | export const transpose = mtx => { 6 | const width = getMatrixWidth(mtx); 7 | 8 | const leftCol = mtx.map(getFirstElement); 9 | 10 | if (width < 2) { 11 | return [leftCol]; 12 | } else { 13 | const rightCols = mtx.map(getAllButFirstElement); 14 | return [leftCol, ...transpose(rightCols)]; 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /test/applyToEntries.test.js: -------------------------------------------------------------------------------- 1 | import { applyToEntries } from "../src"; 2 | import { SQUARE, TALL, WIDE, ROW, COL, BIG } from "./globals"; 3 | 4 | // Declare cube function for testing 5 | const cube = x => x * x * x; 6 | 7 | // SQUARE 8 | it("should cube entries of SQUARE matrix", () => { 9 | const expectedOutput = [[1, 8, 27], [64, 125, 216], [343, 512, 729]]; 10 | expect(applyToEntries(SQUARE)(cube)).toEqual(expectedOutput); 11 | }); 12 | 13 | // ROW 14 | it("should cube entries of ROW matrix", () => { 15 | const expectedOutput = [[1, 8, 27]]; 16 | expect(applyToEntries(ROW)(cube)).toEqual(expectedOutput); 17 | }); 18 | 19 | // COL 20 | it("should cube entries of COL matrix", () => { 21 | const expectedOutput = [[1], [8], [27]]; 22 | expect(applyToEntries(COL)(cube)).toEqual(expectedOutput); 23 | }); 24 | -------------------------------------------------------------------------------- /test/getDiagonals.test.js: -------------------------------------------------------------------------------- 1 | import { SQUARE, TALL, WIDE, ROW, COL, BIG } from "./globals"; 2 | import { getMinorDiagonals, getMajorDiagonals } from "../src/index"; 3 | 4 | /** 5 | |-------------------------------------------------- 6 | | Empty Case 7 | |-------------------------------------------------- 8 | */ 9 | it("should should get minor/major diagonals of an empty matrix", () => { 10 | expect(getMinorDiagonals([[]])).toEqual([[]]); 11 | expect(getMajorDiagonals([[]])).toEqual([[]]); 12 | expect(() => getMinorDiagonals([])).toThrow(); 13 | expect(() => getMajorDiagonals([])).toThrow(); 14 | }); 15 | 16 | /** 17 | |-------------------------------------------------- 18 | | Minor Diagonals 19 | |-------------------------------------------------- 20 | */ 21 | 22 | // SQUARE 23 | it("should get minor diagonals of a square matrix", () => { 24 | const expectedOutput = [[1], [4, 2], [7, 5, 3], [8, 6], [9]]; 25 | expect(getMinorDiagonals(SQUARE)).toEqual(expectedOutput); 26 | }); 27 | 28 | // WIDE 29 | it("should get minor diagonals of a wide matrix", () => { 30 | const expectedOutput = [[1], [4, 2], [5, 3], [6]]; 31 | expect(getMinorDiagonals(WIDE)).toEqual(expectedOutput); 32 | }); 33 | 34 | // TALL 35 | it("should get minor diagonals of a tall matrix", () => { 36 | const expectedOutput = [[1], [3, 2], [5, 4], [6]]; 37 | expect(getMinorDiagonals(TALL)).toEqual(expectedOutput); 38 | }); 39 | 40 | // ROW 41 | it("should get minor diagonals of a row matrix", () => { 42 | const expectedOutput = [[1], [2], [3]]; 43 | expect(getMinorDiagonals(ROW)).toEqual(expectedOutput); 44 | }); 45 | 46 | // COLUMN 47 | it("should get minor diagonals of a column matrix", () => { 48 | const expectedOutput = [[1], [2], [3]]; 49 | expect(getMinorDiagonals(COL)).toEqual(expectedOutput); 50 | }); 51 | 52 | /** 53 | |-------------------------------------------------- 54 | | Major Diagonals 55 | |-------------------------------------------------- 56 | */ 57 | 58 | // SQUARE 59 | it("should get major diagonals of a square matrix", () => { 60 | const expectedOutput = [[7], [4, 8], [1, 5, 9], [2, 6], [3]]; 61 | expect(getMajorDiagonals(SQUARE)).toEqual(expectedOutput); 62 | }); 63 | 64 | // WIDE 65 | it("should get major diagonals of a wide matrix", () => { 66 | const expectedOutput = [[4], [1, 5], [2, 6], [3]]; 67 | expect(getMajorDiagonals(WIDE)).toEqual(expectedOutput); 68 | }); 69 | 70 | // TALL 71 | it("should get major diagonals of a tall matrix", () => { 72 | const expectedOutput = [[5], [3, 6], [1, 4], [2]]; 73 | expect(getMajorDiagonals(TALL)).toEqual(expectedOutput); 74 | }); 75 | 76 | // ROW 77 | it("should get major diagonals of a row matrix", () => { 78 | const expectedOutput = [[1], [2], [3]]; 79 | 80 | expect(getMajorDiagonals(ROW)).toEqual(expectedOutput); 81 | }); 82 | 83 | // COL 84 | it("should get major diagonals of a column matrix", () => { 85 | const expectedOutput = [[3], [2], [1]]; 86 | expect(getMajorDiagonals(COL)).toEqual(expectedOutput); 87 | }); 88 | -------------------------------------------------------------------------------- /test/getSpirals.test.js: -------------------------------------------------------------------------------- 1 | import { SQUARE, TALL, WIDE, ROW, COL, BIG } from "./globals"; 2 | import { getClockwiseSpiral, getCounterClockwiseSpiral } from "../src/index"; 3 | 4 | /** 5 | |-------------------------------------------------- 6 | | Clockwise Spiral 7 | |-------------------------------------------------- 8 | */ 9 | 10 | // SQUARE 11 | it("should get clockwise spiral array for square matrix", () => { 12 | const expectedOutput = [1, 2, 3, 6, 9, 8, 7, 4, 5]; 13 | expect(getClockwiseSpiral(SQUARE)).toEqual(expectedOutput); 14 | }); 15 | 16 | // WIDE 17 | it("should get clockwise spiral array for wide matrix", () => { 18 | const expectedOutput = [1, 2, 3, 6, 5, 4]; 19 | expect(getClockwiseSpiral(WIDE)).toEqual(expectedOutput); 20 | }); 21 | 22 | // TALL 23 | it("should get clockwise spiral array for tall matrix", () => { 24 | const expectedOutput = [1, 2, 4, 6, 5, 3]; 25 | expect(getClockwiseSpiral(TALL)).toEqual(expectedOutput); 26 | }); 27 | 28 | // ROW 29 | it("should get clockwise spiral array for row matrix", () => { 30 | const expectedOutput = [1, 2, 3]; 31 | expect(getClockwiseSpiral(ROW)).toEqual(expectedOutput); 32 | }); 33 | 34 | // COL 35 | it("should get clockwise spiral array for col matrix", () => { 36 | const expectedOutput = [1, 2, 3]; 37 | expect(getClockwiseSpiral(COL)).toEqual(expectedOutput); 38 | }); 39 | 40 | // BIG 41 | it("should get clockwise spiral array for big matrix", () => { 42 | const expectedOutput = [ 43 | 1, 44 | 2, 45 | 3, 46 | 4, 47 | 5, 48 | 6, 49 | 12, 50 | 18, 51 | 24, 52 | 23, 53 | 22, 54 | 21, 55 | 20, 56 | 19, 57 | 13, 58 | 7, 59 | 8, 60 | 9, 61 | 10, 62 | 11, 63 | 17, 64 | 16, 65 | 15, 66 | 14 67 | ]; 68 | expect(getClockwiseSpiral(BIG)).toEqual(expectedOutput); 69 | }); 70 | 71 | /** 72 | |-------------------------------------------------- 73 | | Clockwise Spiral 74 | |-------------------------------------------------- 75 | */ 76 | 77 | // SQUARE 78 | it("should get counter-clockwise spiral array for square matrix", () => { 79 | const expectedOutput = [1, 4, 7, 8, 9, 6, 3, 2, 5]; 80 | expect(getCounterClockwiseSpiral(SQUARE)).toEqual(expectedOutput); 81 | }); 82 | 83 | // WIDE 84 | it("should get counter-clockwise spiral array for wide matrix", () => { 85 | const expectedOutput = [1, 4, 5, 6, 3, 2]; 86 | expect(getCounterClockwiseSpiral(WIDE)).toEqual(expectedOutput); 87 | }); 88 | 89 | // TALL 90 | it("should get counter-clockwise spiral array for tall matrix", () => { 91 | const expectedOutput = [1, 3, 5, 6, 4, 2]; 92 | expect(getCounterClockwiseSpiral(TALL)).toEqual(expectedOutput); 93 | }); 94 | 95 | // ROW 96 | it("should get counter-clockwise spiral array for row matrix", () => { 97 | const expectedOutput = [1, 2, 3]; 98 | expect(getCounterClockwiseSpiral(ROW)).toEqual(expectedOutput); 99 | }); 100 | 101 | // COL 102 | it("should get counter-clockwise spiral array for column matrix", () => { 103 | const expectedOutput = [1, 2, 3]; 104 | expect(getCounterClockwiseSpiral(COL)).toEqual(expectedOutput); 105 | }); 106 | 107 | // BIG 108 | it("should get counter-clockwise spiral array for big matrix", () => { 109 | const expectedOutput = [ 110 | 1, 111 | 7, 112 | 13, 113 | 19, 114 | 20, 115 | 21, 116 | 22, 117 | 23, 118 | 24, 119 | 18, 120 | 12, 121 | 6, 122 | 5, 123 | 4, 124 | 3, 125 | 2, 126 | 8, 127 | 14, 128 | 15, 129 | 16, 130 | 17, 131 | 11, 132 | 10, 133 | 9 134 | ]; 135 | expect(getCounterClockwiseSpiral(BIG)).toEqual(expectedOutput); 136 | }); 137 | -------------------------------------------------------------------------------- /test/globals.js: -------------------------------------------------------------------------------- 1 | export const SQUARE = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; 2 | 3 | export const TALL = [[1, 2], [3, 4], [5, 6]]; 4 | 5 | export const WIDE = [[1, 2, 3], [4, 5, 6]]; 6 | 7 | export const ROW = [[1, 2, 3]]; 8 | 9 | export const COL = [[1], [2], [3]]; 10 | 11 | export const BIG = [ 12 | [1, 2, 3, 4, 5, 6], 13 | [7, 8, 9, 10, 11, 12], 14 | [13, 14, 15, 16, 17, 18], 15 | [19, 20, 21, 22, 23, 24] 16 | ]; 17 | -------------------------------------------------------------------------------- /test/matrixShift.test.js: -------------------------------------------------------------------------------- 1 | import { SQUARE, TALL, WIDE, ROW, COL, BIG } from "./globals"; 2 | import { flipRows, flipCols } from "../src/index"; 3 | 4 | /** 5 | |-------------------------------------------------- 6 | | Simple Transforms 7 | |-------------------------------------------------- 8 | */ 9 | 10 | it("should flip square matrix on a row", () => { 11 | const expectedOutput = { 12 | top: [[7, 8, 9], [4, 5, 6], [1, 2, 3]], 13 | mid: [[7, 8, 9], [4, 5, 6], [1, 2, 3]], 14 | bot: [[7, 8, 9], [4, 5, 6], [1, 2, 3]] 15 | }; 16 | expect(flipRows(SQUARE)).toEqual(expectedOutput.top); 17 | expect(flipRows(SQUARE)).toEqual(expectedOutput.mid); 18 | expect(flipRows(SQUARE)).toEqual(expectedOutput.bot); 19 | }); 20 | 21 | it("should flip square matrix on a column", () => { 22 | const expectedOutput = { 23 | left: [[3, 2, 1], [6, 5, 4], [9, 8, 7]], 24 | mid: [[3, 2, 1], [6, 5, 4], [9, 8, 7]], 25 | right: [[3, 2, 1], [6, 5, 4], [9, 8, 7]] 26 | }; 27 | expect(flipCols(SQUARE)).toEqual(expectedOutput.left); 28 | expect(flipCols(SQUARE)).toEqual(expectedOutput.mid); 29 | expect(flipCols(SQUARE)).toEqual(expectedOutput.right); 30 | }); 31 | -------------------------------------------------------------------------------- /test/transpose.test.js: -------------------------------------------------------------------------------- 1 | import { SQUARE, TALL, WIDE, ROW, COL, BIG } from "./globals"; 2 | import { transpose } from "../src/index"; 3 | 4 | it("should transpose a square matrix", () => { 5 | const expectedOutput = [[1, 4, 7], [2, 5, 8], [3, 6, 9]]; 6 | expect(transpose(SQUARE)).toEqual(expectedOutput); 7 | }); 8 | 9 | it("should transpose a tall matrix", () => { 10 | const expectedOutput = [[1, 3, 5], [2, 4, 6]]; 11 | expect(transpose(TALL)).toEqual(expectedOutput); 12 | }); 13 | 14 | it("should transpose a wide matrix", () => { 15 | const expectedOutput = [[1, 4], [2, 5], [3, 6]]; 16 | expect(transpose(WIDE)).toEqual(expectedOutput); 17 | }); 18 | 19 | it("should transpose a row matrix", () => { 20 | const expectedOutput = [[1], [2], [3]]; 21 | expect(transpose(ROW)).toEqual(expectedOutput); 22 | }); 23 | 24 | it("should transpose a column matrix", () => { 25 | const expectedOutput = [[1, 2, 3]]; 26 | expect(transpose(COL)).toEqual(expectedOutput); 27 | }); 28 | -------------------------------------------------------------------------------- /test/utilities.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | getMatrixHeight, 3 | getMatrixWidth, 4 | getMatrixDimensions, 5 | transpose, 6 | sliceMatrixRows, 7 | sliceMatrixCols, 8 | getMiddleElements, 9 | doMatrixCheck 10 | } from "../src/index"; 11 | import { SQUARE, TALL, WIDE, ROW, COL, BIG } from "./globals"; 12 | 13 | /** 14 | |-------------------------------------------------- 15 | | Tests 16 | |-------------------------------------------------- 17 | */ 18 | 19 | it("should get height of various nArrays", () => { 20 | const nArray = [[1], [2, 3], "abc"]; 21 | 22 | expect(() => { 23 | getMatrixHeight(nArray); 24 | }).toThrow(); 25 | expect(getMatrixHeight(SQUARE)).toEqual(3); 26 | expect(getMatrixHeight(ROW)).toEqual(1); 27 | expect(getMatrixHeight(COL)).toEqual(3); 28 | }); 29 | 30 | it("should should get width of various nArrays", () => { 31 | const nArray = [[1], [2, 3], "abc"]; 32 | 33 | expect(() => { 34 | getMatrixWidth(nArray); 35 | }).toThrow(); 36 | expect(getMatrixWidth(SQUARE)).toEqual(3); 37 | expect(getMatrixWidth(ROW)).toEqual(3); 38 | expect(getMatrixWidth(COL)).toEqual(1); 39 | }); 40 | 41 | it("should get dimensions of various nArrays", () => { 42 | const nArray = [[1], [2, 3], "abc"]; 43 | 44 | const expectedOutput = { 45 | square: { width: 3, height: 3 }, 46 | row: { width: 3, height: 1 }, 47 | col: { width: 1, height: 3 } 48 | }; 49 | 50 | expect(() => { 51 | getMatrixWidth(nArray); 52 | }).toThrow(); 53 | expect(getMatrixDimensions(SQUARE)).toEqual(expectedOutput.square); 54 | expect(getMatrixDimensions(ROW)).toEqual(expectedOutput.row); 55 | expect(getMatrixDimensions(COL)).toEqual(expectedOutput.col); 56 | }); 57 | 58 | it("should get middle elements of an array", () => { 59 | const arrInput = [0, 1, 2, 3, 4]; 60 | const emptyInput = []; 61 | const expectedOutput = { 62 | arr: [1, 2, 3], 63 | emp: [] 64 | }; 65 | 66 | expect(getMiddleElements(arrInput)).toEqual(expectedOutput.arr); 67 | expect(getMiddleElements(emptyInput)).toEqual(expectedOutput.emp); 68 | }); 69 | 70 | it("should slice rows of a square matrix", () => { 71 | const inputs = { 72 | topRows: [0, 2], 73 | botRows: [1], 74 | sameRows: [1, 1], 75 | copy: [] 76 | }; 77 | const expectedOutput = { 78 | topRows: [[1, 2, 3], [4, 5, 6]], 79 | botRows: [[4, 5, 6], [7, 8, 9]], 80 | sameRows: [], 81 | copy: SQUARE 82 | }; 83 | 84 | expect(sliceMatrixRows(SQUARE, ...inputs.topRows)).toEqual( 85 | expectedOutput.topRows 86 | ); 87 | expect(sliceMatrixRows(SQUARE, ...inputs.botRows)).toEqual( 88 | expectedOutput.botRows 89 | ); 90 | expect(sliceMatrixRows(SQUARE, ...inputs.sameRows)).toEqual( 91 | expectedOutput.sameRows 92 | ); 93 | expect(sliceMatrixRows(SQUARE, ...inputs.copy)).toEqual(expectedOutput.copy); 94 | }); 95 | 96 | it("should slice columns of a square matrix", () => { 97 | const inputs = { 98 | leftCols: [0, 2], 99 | rightCols: [1], 100 | sameCols: [1, 1], 101 | copy: [] 102 | }; 103 | const expectedOutput = { 104 | leftCols: [[1, 2], [4, 5], [7, 8]], 105 | rightCols: [[2, 3], [5, 6], [8, 9]], 106 | sameCols: [[]], 107 | copy: SQUARE 108 | }; 109 | 110 | expect(sliceMatrixCols(SQUARE, ...inputs.leftCols)).toEqual( 111 | expectedOutput.leftCols 112 | ); 113 | expect(sliceMatrixCols(SQUARE, ...inputs.rightCols)).toEqual( 114 | expectedOutput.rightCols 115 | ); 116 | expect(sliceMatrixCols(SQUARE, ...inputs.sameCols)).toEqual( 117 | expectedOutput.sameCols 118 | ); 119 | expect(sliceMatrixCols(SQUARE, ...inputs.copy)).toEqual(expectedOutput.copy); 120 | }); 121 | --------------------------------------------------------------------------------