When values are pasted into a bigger range, the values should be repeated as needed to fill the range I am currently working on a project, and I just wrote a piece of it that requires a lot of array manipulation.  Given a text input I want to expand it to fill the number of rows and columns I have.  I worked out a solution, but I am concerned that I am creating too many arrays in my solution.  I am looking for pointers on how I can make this code more efficient.  I always get really lost when trying to refactor things like this.

function fillIn(data,numRows,numCols) {
  // First, split the data by newlines and tabs into an array of arrays.
  var dataArr = data.split('\n').map(function(row) { return row.split('\t'); });

  function fillRows(pRows){
    return [].concat.apply([], new Array(Math.floor(numRows / pRows.length)).fill(pRows));
  }

  function fillCols(pRows) {
    for (var i = 0; i &lt; pRows.length; i++) {
      pRows[i] = [].concat.apply([], new Array(Math.floor(numCols / pRows[i].length)).fill(pRows[i]));
    }
    return pRows;
  }

  function fillSelection(pRows) {
    if (numRows &gt; pRows.length) {
      pRows = fillRows(pRows);
    }
    if (numCols &gt; pRows[0].length) {
      pRows = fillCols(pRows);
    }
    return pRows;
  }

  dataArr = fillSelection(dataArr);

  return dataArr;
}

console.log(fillIn('foo',2,2)); //[['foo','foo'],['foo','foo']]
console.log(fillIn('foo\tbar',4,2)); //[['foo','bar'],['foo', 'bar'],['foo','bar'],['foo','bar']]
console.log(fillIn('foo\nbar',2,4)); // [['foo','foo','foo','foo'],['bar','bar','bar','bar']]
ECMAScript 6

If you're able to use ECMAScript 6 then arrow functions could help clean up some of your code.

var dataArr = data.split('\n').map(row =&gt; row.split('\t'));




Unit Testing

You're logging the results, but it would be extremely helpful for your refactoring process to create some test cases for your function.

function test(input, expected) {
  var result = fillIn(input.data, input.numRows, input.numCols);

  function matrixEquals(m1, m2) {
    if (m1.length !== m2.length) return false;

    for (var r = 0; r &lt; m1.length; r++) {
      if (m1[r].length !== m2[r].length) return false;

      for (var c = 0; c &lt; m1[r].length; c++) {
        if (m1[r][c] !== m2[r][c]) return false;
      }
    }

    return true;
  }

  if (!matrixEquals(expected, result)) {
    throw "Test Failed for: input=" + JSON.stringify(input) +
      ", result=" + JSON.stringify(result);
  }
}

test({
  data: "foo\tbar",
  numRows: 2,
  numCols: 2
}, [
  ["foo", "bar"],
  ["foo", "bar"]
]);

test({
  data: "foo\tbar\nfat\tcat",
  numRows: 4,
  numCols: 4
}, [
  ["foo", "bar", "foo", "bar"],
  ["fat", "cat", "fat", "cat"],
  ["foo", "bar", "foo", "bar"],
  ["fat", "cat", "fat", "cat"]
]);


If you write your test cases well, you'll be able to tell if you broke your function simply by running the test cases.



Do you really even need to create all the arrays?

You could just extend the initial array directly:

function fillIn(data, numRows, numCols) {
  var matrix = data.split('\n').map(row =&gt; row.split('\t'));

  var missingRows = numRows - matrix.length;
  for (var r = 0; r &lt; missingRows; r++) {
    matrix.push(matrix[r].slice());
  }

  var missingCols = numCols - matrix[0].length;
  for (var c = 0; c &lt; missingCols; c++) {
    matrix.forEach(function(row) {
      row.push(row[c]);
    });
  }

  return matrix;
}