Google Sheet copying and paste values and formatting between different sheets - google-sheets

I have been trying to make a script to work for copying and pasting data between two different sheets and I have read all the posts at this forum. Many solutions are too old and don't work anymore and other simply go to different kind of error. I have read Google documentation and tried some stuff, but I could not make it works. I will post here one script I have found and modified that comes closer to what I need, but it fails in some aspects. I need only values (and if possible, formatting too), I don't want to past any of the formulas from the source sheet.
function copiarValores2(){
var source = SpreadsheetApp.openById('1hWTdUNhTEohOqg-UGNGV4XwY7nDOX5Ys9piz256QeIc');
var sourceS = source.getSheets()[0];
var range = sourceS.getRange('A1:S230');
var destination = SpreadsheetApp.openById('15zvklK7NAEBKQcusTwYPEmlH3BNw0t-6MlgQ1fqBuPs');
var destSheet = destination.getSheets()[0];
range.copyValuesToRange(destSheet,1,20,1,230)
}
Running this script I have the following error:
Exception: Target sheet and source range must be on the same spreadsheet.
I don't know how to fix this.
Thanks for attention and help.

Since it is not possible to copy a range to a different spreadsheet directly, You can use this workaround:
Sample Code:
function copiarValores2() {
var source = SpreadsheetApp.openById('1PTRabYcpVY6hLNvUByj9ZJAouogc-uMCMhiexxxxx');
var sourceS = source.getSheetByName("Sheet1");
var destination = SpreadsheetApp.openById('1RPQ7WOTOZZSirf60kP6nULhViCdsuGHcOxxxxxx');
var destSheet = destination.getSheetByName("Sheet1");
//Copy source sheet to destination spreadsheet
newSource = sourceS.copyTo(destination);
//Get the range from the newly copied sheet
var range = newSource.getRange('A1:S230');
//Copy to the destination sheet, starting from cell A1
range.copyTo(destSheet.getRange("A1"));
//Delete copied source sheet
destination.deleteSheet(newSource);
}
What it does?
Open a source spreadsheet and select a sheet to be copied using Spreadsheet.getSheetByName(name)
Open a destination spreadsheet and select a sheet where the values will be pasted using Spreadsheet.getSheetByName(name)
Copy your source sheet to your destination spreadsheet using Sheet.copyTo(spreadsheet). This copied sheet will be your new source sheet which is located in your destination spreadsheet.
The copied sheet is named "Copy of [original name]" in your destination spreadsheet
Select the range you want to copy from your new source sheet then copy the range to your destination sheet using Range.copyTo(destination)
I used this method since you mentioned in your post that you also want to copy the formatting. You can use other copy methods available in Range Class based on your preference
Lastly, delete your new source sheet in your destination spreadsheet using Spreadsheet.deleteSheet(sheet)
OUTPUT:
Source Sheet:
Destination Sheet:

Related

Google Docs, link to dynamic range from Google Sheets

Following this guide, I am able to link data from my Google Sheet into my Google Doc.
https://support.google.com/a/users/answer/9308662?hl=en
Is there a way to have range size update automatically when the table shrinks and grows?
You can do the following:
Create a new named range that includes the current range of the table (ref: Name a range of cells).
Link that named range to your desired location, using Link to data in a spreadsheet.
On your spreadsheet, click Tools > Script editor to open a bound script, and copy the following code. This function retrieves your desired named range and updates it with the current dimensions of your table:
const SHEET_NAME = "Sheet1"; // Change according to your preferences
const NAMED_RANGE_NAME = "MY_RANGE"; // Change according to your preferences
function updateNamedRange() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(SHEET_NAME);
const namedRanges = ss.getNamedRanges();
const namedRange = namedRanges.find(namedRange => namedRange.getName() === NAMED_RANGE_NAME);
const range = sheet.getRange("A1").getDataRegion();
namedRange.setRange(range);
}
Install a time-driven trigger which will fire updateNamedRange with the specified periodicity. You can do that manually (following these steps), or programatically. In order to install this programmatically, copy and run a function like this once:
function installTimeTrigger() {
ScriptApp.newTrigger("updateNamedRange")
.timeBased()
.everyMinutes(1)
.create();
}
Note:
The previous sample is using everyMinutes with the parameter set to 1, so the range will be updated every minute. You can find methods for alternative frequencies here.
In the sample above, the table is in a sheet named Sheet1, with a named range title MY_RANGE, and it starts at cell A1. Change all those in your spreadsheet if that's not your case.
I'm assuming you already know how to link named ranges.

How to get all page names of a spreadsheet file in a column which will keep updating if the page name is changed?

I have a spreadsheet with say following page names:
"Sheet 1"
"Sheet 2"
"Sheet 3"
Afterwards, I changed name of
"Sheet 1" -> "Rough"
Is there anyway for me to get a list of all page names of the file in a spreadsheet page in a column?
Issue:
You want to trigger an action when a sheet name is modified (sheet here refers to a tab, or to a page, as you seem to call it). Sheets formulas will not get triggered by a sheet name change.
Solution:
You can accomplish this by installing an Apps Script onChange trigger, which will fire a function you specify when the spreadsheet's content or structure is changed (e.g., when a sheet name is changed). To do this, you can follow these steps:
In your spreadsheet, select Tools > Script editor to open a script bound to your file.
Create an Apps Script function to do the following: (1) retrieve all the sheet names, (2) write these sheet names to a specified sheet. You could use this function, for example. Copy it to the script editor and save the project:
function onChangeTrigger() {
var ss = SpreadsheetApp.getActive(); // Get current spreadsheet
var sheetNames = ss.getSheets().map(sheet => [sheet.getName()]); // Get sheet names
var destSheet = ss.getSheetByName("All Tab Names"); // Change according to your preferences
destSheet.getRange("A2:A").clearContent(); // Remove previous content
destSheet.getRange(2, 1, sheetNames.length).setValues(sheetNames);
}
Install the onChange trigger to fire the function above, either manually, following these steps, or programmatically, by running this function once:
function createOnChangeTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger("onChangeTrigger")
.forSpreadsheet(ss)
.onChange()
.create();
}
Note:
The function onChangeTrigger writes all sheet names to column A of a sheet called All Tab Names. Please change that to your sheet name. Also, check this if you want to change the range to which the sheet names are written.
Reference:
onChange trigger
You could install this Addon Formulas to use one of its functions =UTIL_SHEETNAME() within a cell of all your sheets (let say in A1 of every tab), then you would have a master sheet for listing your tabs and where you reference each formula you've used (=Sheet1!A1; =Sheet2!A1; etc...). But this function doesn't seem to update quickly though.
Alternatively, you could write a code (Apps Script) to list all your tabs, which has been answered everywhere. If you need help with the latter, add in your tags 'Google-Apps-Script' to add more visibility to your question.

Google Sheets Export to Excel values instead of formulas

I have a google sheet where the cells in the first tab pull data from cells on the second tab.
for example Sheet1 cell A1 has =Sheet2!A1
This is true for every cell on Sheet1
When I do a File - Download As - Microsoft Excel (.xlsx)
It exports the cells with formulas. Is there a way to export the sheets as values and not formulas
In this case, Sheet1 cell A1 would not contain =Sheet2!A1 but the value of =Sheet2!A1?
You can copy your original google spreadsheet and, in the copy, change the formula for the first cell for each tab to import data from the original one:
=IMPORTRANGE("spreadsheet id","'tab name'!range")
Ex:
=IMPORTRANGE("1C-PS4wAHS8ssCNgVDfOsssREAz7PjuQGX23Rk0sssss","'measurement with spaces'!A12:F44")
The ID you can get via original spreadsheet URL:
https://docs.google.com/spreadsheets/d/1C-PS4wAHS8ssCNgVDfOsssREAz7PjuQGX23Rk0sssss/edit#gid=99999999
The exported xlsx file from the copy will have only the values
#fabceolins answer is simple and good for normal scenarios, i noticed however Excel will still contain reference to IMPORTRANGE formula which can cause access issues.
I created Google App script to copy in the following method.
If you can use Google App scripts, add the following functions:
function update_view(dup_id, TL="A1", BR="Z991") {
// Open current Sheet
var ss = SpreadsheetApp.getActiveSpreadsheet()
// Supply a duplicate google doc ID. This document will be exported to excel
var ds = SpreadsheetApp.openById(dup_id)
// UI element for notifying in the google sheets
var ui = SpreadsheetApp.getUi()
//Copy each sheet one by one
var sheets = ss.getSheets();
for (i=0; i<sheets.length; i++) {
src_sheet = sheets[i];
sheet_name = src_sheet.getName();
// If same sheet exists in the destination delete it and create an empty one
dst_sheet = ds.getSheetByName(sheet_name);
if (dst_sheet != null) {
ds.deleteSheet(dst_sheet)
}
dst_sheet = ds.insertSheet(sheet_name);
//set column width correctly
for(j=1; j<=src_sheet.getLastColumn(); j++){
dst_sheet.setColumnWidth(j, src_sheet.getColumnWidth(j))
}
src_range = src_sheet.getRange(TL + ":" + BR);
dst_range = dst_sheet.getRange(TL + ":" + BR);
//Note: DisplayValues is set as Values, formulas are removed in dup sheet
dst_range.setValues(src_range.getDisplayValues());
//Nice to haves for formatting
dst_range.setFontColors(src_range.getFontColors());
dst_range.setFontStyles(src_range.getFontStyles());
dst_range.setBackgrounds(src_range.getBackgrounds());
dst_range.setHorizontalAlignments(src_range.getHorizontalAlignments());
dst_range.setVerticalAlignments(src_range.getVerticalAlignments());
dst_range.setWraps(src_range.getWraps());
dst_contents_range = dst_sheet.getDataRange();
dst_contents_range.setBorder(true, true, true, true, true, true);
}
//Completed copy, Now open the dup document and export.
ui.alert("Backup Complete, Please open " + dup_id + " sheet to view contents.")
}
function update_mydoc_view(){
// https://docs.google.com/spreadsheets/d/<spreadsheet_id>/
update_view("<spreadsheet_id>")
}
To run the function, go to tools->macros->import , import the function and run update_mydoc_view().
After it is completed, export the google sheet into an excel document.
If you want to download a single sheet spreadsheet, instead of download it as .XLSX, download it as .CSV.
If by open the .CSV file by double clicking it shows strange characters, the default encoding of your computer is different from the one used by the Google servers, to fix this do the following:
Open Excel
Click File > Open
Select the .CSV file
The import wizard will be shown. One of the steps will allow you to select the file encoding, select UTF-8.
Once you finish with the import wizard save your file as .XLSX
Related
Microsoft Excel mangles Diacritics in .csv files?
Is it possible to force Excel recognize UTF-8 CSV files automatically?
I did the following and it worked for me :
Duplicate the file
CTRL+A > CTRL+X > CTRL + V Paste Value only (from the paste icon displayed after pressing CTRL+V)
If you want to keep only the values from Sheet1, just select the data in the sheet, copy it, open a new Excel work book and when you paste, rather than using the conventional hotkeys Ctrl+V, right click cell A1 and select Paste values under the Paste options category of the right click menu.
If the problem is that the downloaded Excel does not have functional formulas that take information from your second sheet and instead show the formulas as text, do what the previous commenter said. check your view to make sure you are not in formula view. use the hotkey Ctrl+ ~, or go to the view tab to check your view options.
By default this is what happens - you will see the values not the formula.
Are you sure you are not in formula view in Excel?
If you check "Show formula" it will switch in formula view.
Or generally speaking you can try those:
MS Excel showing the formula in a cell instead of the resulting value
I would be surprised if it was indeed a google sheet problem - it's about Excel display.

Want an email sent to me every time conditional formatting is applied

I have a spreadsheet that is automatically updated with tweets from certain Twitter lists. I have conditional formatting applied to the sheet that if there is a swear word within one of the tweets, it will highlight that particular cell red.
What I want is to have the cell (or even the entire spreadsheet) emailed to me whenever conditional formatting is applied. Is this possible?
Also, is there a way to have the spreadsheet's contents go into an "archive" sheet each day so that the main sheet has fresh content each day?
Thanks for your help!
So far, I've been working on the "archiving" script. What I have so far is below:
function importData() {
var ss = SpreadsheetApp.getActiveSpreadsheet(); //source ss
var sheet = ss.getSheetByName("Twitter"); //opens the sheet with your source data
var values = sheet.getRange("A:G").getValues(); //gets needed values
var ts = SpreadsheetApp.openById("1g5XaIycy69a3T2YcWhcbBy0hYrxSfoEEz8c4-zP63O8"); //target ss - paste your key
ts.getSheetByName("Archives").getRange("A:G").setValues(values);}
It says range height in line 11 is wrong.
Here is my snippet of code I'm trying to use to get an email when a swear word is used. I feel like I'm on the cusp, but not quite there yet.
function onEdit(event)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var cell = ss.getActiveCell();
var range = sheet.getRange(cell.getRow(),1,1,sheet.getDataRange().getLastColumn());
var note = cell.getBackground()
var email = "antadrag#gmail.com";
var subject = "Notice of possible inappropriate tweet";
message = cell.getValue();
if(message.contains ("piss")) {
range.setBackgroundRGB(255, 0, 0);
MailApp.sendEmail(email, subject, message);
}
};
To answer your first question, I agree with Dougs suggestion in the comments. The trigger you use to detect a swearword would be the best place to call a function to email you, rather then waiting for a conditional formatting change.
With regards to your query on how to archive the data, your making life a little too difficult for yourself with fetching the ranges and copying them to sheets etc. The 'copyTo()' operation is your friend here, as that will copy the entire sheet to another spreadsheet while preserving conditional formatting.
Here's a sample of how this could possibly work:
function importData() {
var ss = SpreadsheetApp.getActiveSpreadsheet(); //source ss
var sheet = ss.getSheetByName("Twitter"); //opens the sheet with your source data
var ts = SpreadsheetApp.openById('SHEET ID');//Opens destination spreadsheet
sheet.copyTo(ts);//Copy the data to the destination sheet.
sheet.clear();//Clear the source sheet.
//Past this point it's optional, and only if you want to rename the destination sheet.
ts.getSheetByName("Copy of Twitter").activate();//Get the destination sheet by name.
var date = new Date().getDate();//Random variable for the sheets new name
ts.renameActiveSheet(date);//Set the new sheets name.
}
This will open your source sheet, copy the data to a new spreadsheet of your choice and clear the source sheet of data. Optionally, I've added a few lines that rename the destination sheet to a variable of your choice so that it's not easy to understand when it was copied (I suggest date or time, but it can be whatever you want really).

Force IMPORTRANGE to update at certain intervals

I saw several complains about the delay of updating data through IMPORTRANGE in Google Sheets but I need the opposite and don't want the second sheet to get updated automatically, just update at the end of the day for example.
The code is already like this:
=IMPORTRANGE("The Key","The_Page!B:D")
I hacked around this by, on both spreadsheets by creating a refresh loop using a NOW cell, with both spreadsheets crossreferencing each other's NOW cell.
When the original sheet gets appended with a form submission or something, it updates its own NOW cell, and reupdates own IMPORTRANGE cell. The second spreadsheet follows suit, updating its own NOW cell to provide the original sheet with the correct data. Because the second spreadsheet has updated itself, it also updates the main IMPORTRANGE which refreshes the data you want it to display in the first place, as well as the IMPORTRANGE cell which gets the NOW cell from the original spreadsheet
At least, I'm pretty sure that's how it works. All I know, and all I care about, frankly, is that it works
Maybe you need to use the script editor and write a simple function of the kind:
function importData()
{
var ss = SpreadsheetApp.getActiveSpreadsheet(); //source ss
var sheet = ss.getSheetByName("The_Page"); //opens the sheet with your source data
var values = sheet.getRange("B:D").getValues(); //gets needed values
var ts = SpreadsheetApp.openById("The Key"); //target ss - paste your key
ts.getSheetByName("Name of the target sheet").getRange("B:D").setValues(values);
}
And then add a time-driven trigger to this project (Resources > Current project's triggers > Add a new one).

Resources