Copy and Move Data Between Google Sheets with Apps Script
Copying data between spreadsheets is a common task — archiving completed records, consolidating data from multiple sources, or syncing a master sheet with department files. Apps Script handles all of these cleanly.
Copy a range from one sheet to another (same spreadsheet)
functionmoveCompletedRows(){const ss =SpreadsheetApp.getActiveSpreadsheet();const source = ss.getSheetByName('Tasks');const target = ss.getSheetByName('Completed');const data = source.getDataRange().getValues();const statusCol =3;// Column D (0-indexed)// Collect rows to move, iterate from bottom to avoid index shiftingfor(let i = data.length-1; i >=1; i--){if(data[i][statusCol]==='Done'){ target.appendRow(data[i]); source.deleteRow(i +1);}}Logger.log('Completed rows moved to Completed sheet.');}
functionsyncNewRows(){const sourceSheet =SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Source');const targetSheet =SpreadsheetApp.openById('TARGET_SS_ID').getSheetByName('Target');const sourceData = sourceSheet.getDataRange().getValues();const targetData = targetSheet.getDataRange().getValues();// Use first column as unique key (e.g. ID)const existingIds =newSet(targetData.map(r=> r[0]));const newRows = sourceData.slice(1).filter(row=>!existingIds.has(row[0]));if(newRows.length>0){ targetSheet.getRange( targetSheet.getLastRow()+1,1, newRows.length, newRows[0].length).setValues(newRows);Logger.log(`Synced ${newRows.length} new row(s).`);}else{Logger.log('No new rows to sync.');}}
Duplicate a sheet within the same spreadsheet
functionduplicateSheet(){const ss =SpreadsheetApp.getActiveSpreadsheet();const sheet = ss.getSheetByName('Template');const copy = sheet.copyTo(ss); copy.setName('Report - '+newDate().toLocaleDateString());// Move it to the end ss.moveActiveSheet(ss.getNumSheets());Logger.log('Sheet duplicated: '+ copy.getName());}
Consolidate data from multiple spreadsheets into one
functionconsolidateSheets(){const sourceIds =['SPREADSHEET_ID_1','SPREADSHEET_ID_2','SPREADSHEET_ID_3',];const masterSheet =SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Master'); masterSheet.clearContents();let firstFile =true; sourceIds.forEach(id=>{const ss =SpreadsheetApp.openById(id);const sheet = ss.getSheets()[0];// First sheetconst data = sheet.getDataRange().getValues();if(firstFile){// Include header from first file only masterSheet.getRange(masterSheet.getLastRow()+1,1, data.length, data[0].length).setValues(data); firstFile =false;}else{// Skip header row for subsequent filesconst rows = data.slice(1);if(rows.length>0){ masterSheet.getRange(masterSheet.getLastRow()+1,1, rows.length, rows[0].length).setValues(rows);}}});Logger.log('Consolidation complete. Total rows: '+(masterSheet.getLastRow()-1));}
Tips
SpreadsheetApp.openById() requires the script to have permission to access the target file. Share it with the script owner's account.
Writing all rows at once with setValues() is much faster than looping with appendRow().
When moving rows by deleting them from the source, always iterate from bottom to top to avoid row index shifting.
Add SpreadsheetApp.flush() after large writes to force changes to be committed immediately.