Reputation: 18556
When operating (copying/moving) ranges containing merged cells, I always get the error “Your paste overlaps with merged cells. Please unmerge the cells and try again”. But when trying to unmerge the cells in the range using Range#breakApart
, I get another error: “The selected cells cannot be merged.”, which is even more confusing since I’m not trying to merge anything, I’m only trying to break the cells apart.
Upvotes: 4
Views: 8930
Reputation: 1334
Inspired by Raphael's answer, this is how to unmerge all cells in a sheet. Here's what I used for data imports that merged cells that had adjacent null values, i.e. I was unmerging cell a1 with cell b1 where a1 had a value and b1 did not:
function unmergeSheet() {
let mySheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
let myRange = mySheet.getDataRange()
myRange.breakApart();
}
Updated with Juuso Nykänen's suggestion
Upvotes: 0
Reputation: 18717
Here's the simplest way I've found:
function unmerge() {
var sheetName = 'Sheet1'; // Change!
var a1Notation = 'A1:B15'; // Change!
var range = SpreadsheetApp.getActive().getSheetByName(sheetName).getRange(a1Notation);
var mergedRanges = range.getMergedRanges();
for (var i = 0; i < mergedRanges.length; i++)
{
mergedRanges[i].breakApart();
}
}
Upvotes: 0
Reputation: 31
This function breaks apart the range of merged cells that partially intersects with the provided cell or range. It uses Sheets advanced service to identify all merged ranges in a given sheet, as suggested by Adam Morris (@clssrmtechtools) in this thread.
Demo spreadsheet here.
/**
* Breaks apart merged cells in range
* @param {sheet} sheet Sheet of range
* @param {range} rangeToSplit Cell or cells that intersect with whole merged range
*/
function breakApartAux(sheet, rangeToSplit) {
// Current spreadsheet & sheet
let ss = SpreadsheetApp.getActiveSpreadsheet();
let activeSSId = ss.getId();
let activeSId = sheet.getSheetId();
// Get sheet's merges using advanced sheets service
let merges = Sheets.Spreadsheets.get(activeSSId).sheets.find(s => s.properties.sheetId == activeSId).merges;
if (merges == undefined) SpreadsheetApp.getUi().alert('No merged cells found in sheet.');
else {
// Logger.log(merges);
// Cells to merge R/C
let rowS = rangeToSplit.getRow();
let rowE = rowS + rangeToSplit.getNumRows() - 1;
let colS = rangeToSplit.getColumn();
let colE = colS + rangeToSplit.getNumColumns() - 1;
// Find overlapping merged range
// Advanced service merged ranges start in 0 and are right-open [..)
let merge = merges.find(m => {
let mRowS = m.startRowIndex + 1;
let mRowE = m.endRowIndex;
let mColS = m.startColumnIndex + 1;
let mColE = m.endColumnIndex;
// Check for overlapping
return ((rowS >= mRowS && rowS <= mRowE) ||
(rowE <= mRowE && rowE >= mRowS) ||
(rowS < mRowS && rowE > mRowE)) &&
((colS >= mColS && colS <= mColE) ||
(colE <= mColE && colE >= mColS) ||
(colS < mColS && colE > mColE));
})
// Overlapping range?
if (merge == undefined) SpreadsheetApp.getUi().alert('No merged cells found in specified range.');
else {
// Break apart whole range
ss.getActiveSheet().getRange(merge.startRowIndex + 1,
merge.startColumnIndex + 1,
merge.endRowIndex - merge.startRowIndex,
merge.endColumnIndex - merge.startColumnIndex).breakApart();
}
}
}
Upvotes: 1
Reputation: 18556
Turns out breakApart
only works when the range it is called on encompasses all merged cells, not just parts of the merge area.
This would not be too bad. The problem starts once you realize that there is no way in the API to get the ranges of merged cells. So the best way to solve this (that I’ve found so far) is just to gently extend your range until no error occurs:
var breakRange = myRange;
for(;;) {
try {
breakRange.breakApart();
break;
} catch(e) {
breakRange = mySheet.getRange(
breakRange.getRowIndex(),
breakRange.getColumnIndex(),
Math.min(
breakRange.getHeight()+5,
mySheet.getMaxRows()-breakRange.getRowIndex()+1
),
Math.min(
breakRange.getWidth()+5,
mySheet.getMaxColumns()-breakRange.getColumnIndex()+1
)
);
}
}
(I’ve added five rows/cols instead of one since getting ranges seems to be a rather expensive process). This works quite well so long as you don’t have additional merged cells being added as you expand your search area.
When it comes to merged cells in spreadsheets, the Google Apps Scripts API is broken fundamentally in at least the following ways:
breakApart
only returns the original range “for chaining”). This makes it impossible to split merges and then have each cell contain the content of the original merge cell.breakApart
in a range that encompasses only a part of a merge cell should still break apart the entire merge instead of throwing an exception.All in all, the API as it currently stands only works for spreadsheets whose layout and usage the script author herself has complete control over.
Upvotes: 6