Google Sheets - Horizontal merge identical cells i

2020-03-06 02:38发布

问题:

I am working towards updating a sample of code seen at: Merge cells with same words Provided by @Tanaike

I am essentially trying to do the same thing as the sample code above, but with one difference in that I am hoping to pivot this to work horizontally on like columns, instead of vertically against like rows.

I've done my best to flip the functionality, but I am very much a beginner and have run into an impasse. The below is my edited code snippet, and I've marked the particular line where my understanding goes out the window, so I assume there is something wrong there, or so.

function mergeMonths() {
  var start = 6; // Start row number for values.
  var c = {};
  var k = "";
  var offset = 0;
  var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("BlockingChart1");

  // Retrieve values of column B.
  var data = ss
    .getRange(4, start, 1, ss.getLastColumn())
    .getValues()
    .filter(String);

  // Retrieve the number of duplicate values. //This is where my eyebrow starts to raise.
  data.forEach(function(e) {
    c[e[0]] = c[e[0]] ? c[e[0]] + 1 : 1;
  });

  // Merge cells.
  data.forEach(function(e) {
    if (k != e[0]) {
      ss.getRange(4, start + offset, 1, c[e[0]]).merge();
      offset += c[e[0]];
    }
    k = e[0];
  });
}

Just to be 100% clear: I am hoping to check cells in a given (sorted) row for cells with identical contents that are repeated and then merge the repeated cells. (I am working on a calendar type chart, at a weekly level per cell, but want to label the top to be months, so merging the repeating "...Dec,Jan, Jan, Jan, Jan, Feb..." into headers.

If anyone wants to point me to some reading which may help me along my journey, or could lend a hand, that would be greatly appreciated.

回答1:

If I understand correctly you want to transform a row of cells that looks like this:

Jan | Jan | Feb | Feb | Feb | Mar | Apr | Apr | Apr |

into this:

Jan | Feb | Mar | Apr |

If so, I can help!

Step 1. Getting Data

I think part of your issue comes from your call to getRange(row, column, numRows, numColumns).

In a comment you say var start = 6 is the start row, but you put it in the column spot. Not sure what you intended, but it might be more clear to have a startRow and a startCol variable. Eg,

var startRow = 1;
var startCol = 1;

// Later...
ss.getRange(startRow, startCol, 1, ss.getLastColumn())

Calling Range.getValues() in a Google Script returns a two dimensional "array-of-arrays." With the example cells I provided you would get something like:

data = [['Jan', 'Jan', 'Feb', 'Feb', 'Feb', 'Mar', 'Apr', 'Apr', 'Apr']]

Since we're just dealing with one row, we can take the first item of this outer array and call that data, simplifying things a bit. So I would do:

var data = ss
 .getRange(startRow, startCol, 1, ss.getLastColumn())
 .getValues()
 .filter(String)[0];

Which gives you:

data = ['Jan', 'Jan', 'Feb', 'Feb', 'Feb', 'Mar', 'Apr', 'Apr', 'Apr']

Step 2. Counting duplicate values

The next bit of code deals with counting how many times each month is duplicated. With our nice, one-dimensional array we can make the code a little simpler:

data.forEach(function(e) {
  c[e] = c[e] ? c[e] + 1 : 1;
});

Here we're looping over the array data, dealing with each entry "e" in turn. (e will be "Jan", then "Jan" again, then "Feb"). We're storing the counts in an object, c which starts off blank and will look like this when we're finished:

c = {Feb: 3.0, Apr: 3.0, Jan: 2.0, Mar: 1.0}

The ? and : stuff is fancy JavaScript ternary syntax, which is basically saying:

    c[e] = c[e] ? c[e] + 1 : 1;
   [-----] [-----][-------][---]
      |       |       |      |
      |       v       |      |
(1) "have we already created an entry in c for this month e (eg, "Jan")?
      |               |      |
      |               v      |
(2) "if so, COUNT is the current value (look it up) plus 1
      |                      |
      |                      v
(3)   |            "if not, COUNT is 1"
      |
      v
(4) "store the COUNT we found in c"

Step 3. Merge duplicate values

The final step could look like this:

data.forEach(function(e) {
  if (e != k) {
    ss.getRange(startRow, startCol + offset, 1, c[e]).merge();
    offset += c[e];
  }
  k = e;
});

Again, things are a tiny bit simpler because data is now a 1d array instead of a 2d array.

Complete Code

function mergeMonths() {

  var startRow = 1;
  var startCol = 1;
  var c = {};
  var k = "";
  var offset = 0;

  var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("BlockingChart1");

  // Get data from sheet
  var data = ss
  .getRange(startRow, startCol, 1, ss.getLastColumn())
  .getValues()
  .filter(String)[0];

  // Count duplicates
  data.forEach(function(e) {
    c[e] = c[e] ? c[e] + 1 : 1;
  });

  // Merge duplicate cells
  data.forEach(function(e) {
    if (e != k) {
      ss.getRange(startRow, startCol + offset, 1, c[e]).merge();
      offset += c[e];
    }
    k = e;
  });

}

Quick Tip

One final tip: The logger is super helpful for seeing what's going on in your code. When you're testing your code I would stick lines like:

Logger.log("data: %s", data);

and

Logger.log("e: %s", e);

All over the place, then check the Google Scripts log (view > logs) to understand what your variables are at different points.