I have two spreadsheets, one client facing and one internal facing, that use IMPORTRANGE and QUERY + IMPORTRANGE to display data between one another.
The internal sheet is used to write copies for social media posts, quality check them and give approval. Once approval is given the client sheet needs to update to show the copies on the identically named pages AND in the correct rows.
Approval is given by ticking a checkbox in the internal sheet and the QUERY + IMPORTRANGE formula then pulls that into the client sheet. However, my abilities stop where I have to display this in the correct row position no matter which of the tickboxes are checked.
=iferror(QUERY(IMPORTRANGE("https://docs.google.com/spreadsheets/d/1CIXhcuTigYbggmPjBKpvnH7P-cjBDQfSIcmKN6_0T5M/edit#gid=2018991957","Cloud in FS Survey!C2:N"),"SELECT Col1, Col2, Col3, Col4, Col5, Col6 WHERE Col11 = TRUE"),"No copies approved")
I've seen some discussions of having a second sheet for of the categories (there are 8 in the non-redacted document) and using VLOOKUP to match an ID to the imported row of data, hence I've created 'Row' columns in all tabs on both sheets with the unique row number. However, I want to avoid having hidden tabs in the client facing sheet where they can be revealed by the client.
Any assistance would be appreciated.
Many thanks,
Mark
There are 2 ways to deal with it. If you a add serial number in sheet 1 where approvals are given, you may add another element to your query by adding order by serial no. column ASC
Else you could do the same with order by date column ASC
This way new additions will add below the imported data because they will always be in chronology and not mess up the order of older data.
Having direct importranges where static data exists is always risky.
Alternately, you could also use ID thing without creating an additional hidden sheet, direct vlookups with importrange nested inside the vlookup.
For example, =arrayformula(vlookup(search key, importrangexxx, index, false)
The best solution would be to have a hidden sheet. But all of the above can be decent alternate if you're not dealing with thousands of rows.
If you don't want to use helper sheet or column, you can use Apps Script and onEdit Trigger.
Using onEdit Trigger, you can run a function automatically when a user click the checkbox. Inside that function is a list of commands that will write data to the client side.
Example:
To start with Apps Script:
Go to Tools -> Script Editor.
In your script editor, delete the code in Code.gs
Paste the code provided below.
Click Save.
Code:
function showToClientSide(e) {
var val = e.value;
var range = e.range;
var row = range.getRow();
var col = range.getColumn();
var sheet = range.getSheet();
var clientSS = SpreadsheetApp.openById("Insert Client Sheet ID here")
if(val == "TRUE" && row > 1 && col == 13){ // check if checkbox is checked
var data = sheet.getRange(row, 3, 1, 9).getValues(); //get data from internal
var sh = clientSS.getSheetByName("Cloud in FS Survey"); //client sheet
sh.getRange(row, 3, 1, 9).setValues(data); //write internal data to client
}else if(val == "FALSE" && row > 1 && col == 13){
var sh = clientSS.getSheetByName("Cloud in FS Survey"); //client
sh.getRange(row, 3, 1, 9).clearContent(); //delete data when unchecked
}
}
To setup your Installable Trigger (onEdit):
In the left menu of your Apps Script, click Triggers
Click Add Trigger
Copy the setup below.
Save and Authorize the script.
Testing:
Internal Sheet:
Client Sheet:
Checkbox checked:
Internal:
Client:
Checkbox unchecked:
Internal:
Client:
References:
Installable Triggers
Event Objects
Class Range
Class Sheet
Related
I've got a large document with a built-in Lookup sheet, which allows me to query all sheets in order to find row information about a particular person
Example doc here: https://docs.google.com/spreadsheets/d/1cn-LnKCauoBMxLCaJyfTg8t0qS2bsMAAYqTE5FHA-4M/edit#gid=1006641306
This sheet has a query where I combine LOTS of sheets together, using a custom formula sheetname() to grab the name of the sheet and also to dynamically select all the rows of that sheet (it also adds the sheet name as a column). This worked fine for a few sheets, but now I'm over 30 sheets, and it's exhausting to add more to this formula
Is there any way to shorten this formula so that I don't need to add a new line for every single new sheet I want to query?
=QUERY(
{{indirect(sheetnumber(3)&"!$A$3:$e$"&counta(indirect(sheetnumber(3)&"!A3:A"))), TRANSPOSE(SPLIT(REPT(sheetnumber(3)&"♦",
ROWS(indirect(sheetnumber(3)&"!$A$3:$e$"&counta(indirect(sheetnumber(3)&"!A3:A"))))), "♦"))};
{indirect(sheetnumber(4)&"!$A$3:$e$"&counta(indirect(sheetnumber(4)&"!A3:A"))), TRANSPOSE(SPLIT(REPT(sheetnumber(4)&"♦", ROWS(indirect(sheetnumber(4)&"!$A$3:$e$"&counta(indirect(sheetnumber(4)&"!A3:A"))))), "♦"))};
{indirect(sheetnumber(5)&"!$A$3:$e$"&counta(indirect(sheetnumber(5)&"!A3:A"))), TRANSPOSE(SPLIT(REPT(sheetnumber(5)&"♦", ROWS(indirect(sheetnumber(5)&"!$A$3:$e$"&counta(indirect(sheetnumber(5)&"!A3:A"))))), "♦"))}
},
"select Col6,Col2,Col3,Col4,Col5
where lower(Col1) contains '"& lower($A$2) &"'
",0)
Thanks in advance for your ideas!
This is a very common question.
The real answer is "Don't build sheets like this". That is - data entry should always happen on ONE tab, forever, for all time. Adding new sheets for new data entry is a method left over from leather bound ledgers in the 16th century.
But most people do not like that answer. So I wrote a custom function called "StackRange()". Especially used in conjunction with another simple custom function "SheetNames()" it will do what you want.
Here is a View Only version. Feel free to File>Make a Copy so you can access the script. You will see the function implemented on two tabs, one that uses "SheetNames()" one that uses a rangeRef to accomplish the same thing.
Here is the code for StackRange:
/**
* Stacks a given range from a list of sheets. Ranges can be closed (A2:G10), or open-ended (C2:T)
* Example: =STACKRANGE("A2:C",Settings!B2:B10,1,TRUE)
* #param {"A2:C"} range The range in each sheet that will be stacked ("string")
* #param {Settings!B2:B10} tabs A range reference or array listing the tabs from which to stack
* #param {1} headers The number of header rows
* #param {TRUE} source TRUE to pre-pend the source tab name to each row
* #customfunction
*/
function STACKRANGE(range,tabs,headers,source) {
var ss = SpreadsheetApp.getActive();
var sheetNames = ss.getSheets().map(e=>e.getName());
var currentSheetName = ss.getActiveSheet().getName();
tabs = tabs.filter(e=>e[0] && sheetNames.indexOf(e[0])!=-1 && e[0]!=currentSheetName);
var values = tabs.flat().map((e,i)=>ss.getSheetByName(e).getRange(range).getValues().map(function (g,k){
if(!source){return g}else if(k<headers && i==0 && headers){return ['Source',...g]}else{return [e,...g]}
}).filter((f,j)=>j>=headers || i==0)).flat();
if(source){values = values.filter(e=>e.slice(1).join(''))}else{values = values.filter(e=>e.join(''))}
return values;
}
I'm using arrayformulas to populate certain columns of my sheet, depending on values in other sheets. Occasionally users accidentally will put some value in these columns, and this will stop arrayformula from working. I want to protect these columns, but still allow adding/editing/deleting rows.
Consider this example spreadsheet: I want Id row to be protected, but allow addition/deletion of rows.
https://docs.google.com/spreadsheets/d/1Dnj7OE5XZL09gllHVwPgv-5GRoM-lxVCxTCI_-kURdM/edit#gid=0
Is this possible at all with Google Sheets?
You can't directly disable input but you can use Data Validation instead
By going to Data > Data Validation and filling it with the following:
Cell range: YourSheet!C2:C
Criteria: Custom formula is - =C2:C = A2:A & "["&B2:B&"]"
On invalid data: Reject input
Appeareance: Optional message
Once you've done this, try to fill some cell in the C column and you'll see a message: There was a problem - Your optional message
As a different approach you can use Apps Script Simple Triggers
By going to Tools > Script Editor and copying the following code:
function onEdit(e) {
var column = e.range.getColumn();
var sheet = SpreadsheetApp.getActiveSheet();
if (column === 3) {
e.range.setValue("");
}
}
Which is more like an "undo" function.
References
Simple Triggers
Event Objects > onEdit
rows could be added by brute force with this formula:
=ARRAYFORMULA(ROW(INDIRECT("1:"&ROWS(A:A)+1)))
but escalation cant be controlled
I'm new to this,
I have 2 google spreadsheets:
Spreadsheet A: The active sheet Containing multiple tabs with information to be Pushed to B.
Spreadsheet B: A spreadsheet with a single tab. The same headers and structure as spreadsheet A.
Based on the user selecting the answer "Yes" in the first column of any of the 1 tabs in Spreadsheet A, I would like that entire row to move over to Spreadsheet B.
I have modified a script that works on a single spreadsheet (ie moving rows from tab to tab) to attempt to get it to work between spreadsheets:
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var tss = SpreadsheetApp.openById('B').getSheetByName('Sheet 1');
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if(r.getColumn() == 1 && r.getValue() == "Yes") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var target = tss.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).moveTo(target);
}
}
Probably needless to say, this yields no result. Having searched through a number of posts and forums I only see individuals posting about how to move rows between tabs but not between entirely separate spreadsheets. Is it even possible to do this? If so, what am I doing wrong in the script?
Thank you so much for anyone who takes the time to assist.
Anthony
Following a dialogue with the OP in the comments section, it was indicated that the original spreadsheet did not to be kept secret.
Consequently, the desired functionality can be provided by using a combination of IMPORTRANGE() and QUERY() in the spreadsheet with no need to use Google App Script. For instance,
=QUERY(IMPORTRANGE(url,range),"select A where B matches 'Yes'") or similar
This imports data from a second spreadsheet and then the QUERY() function acts as a way of filtering the imported range by certain criteria.
Once the imported range is authorised, the editors of the spreadsheet can access it by, e.g. removing or modifying the query. You could prevent this by protecting that particular cell, if needed.
I have spreadsheet similar to this:
I'd like to remove all duplicates of row based on the first column data.
So from this screenshot row, 1 and 2 would be kept, and row 2 would be removed. Any help would be greatly appreciated.
P.S. In my case I have columns from A to AU and rows from 2 to 9500.
Thank you.
Maya's answer and the solution AJPerez linked both work.
You can also use Filter View, which doesn't require deleting rows or creating new rows/sheets.
First create a helper column, say to the left of all your data. If your data starts on row 1, then create a blank row above all your data; if not, you are fine. Afterwards, on the first row where you have data, say row 2, write in the formula
=iserror(match(B2,B$1:B1,0))
Replace "2" with the row number of the first row of your data and "1" with that number minus 1. Also populate the rest of the column with the formula. (Unfortunately, arrayformula doesn't work here.) The formula outputs TRUE when the entry in B# has not occurred in cells above.
Note that this assumes your data now starts with column B and column B is where you want the filter to base. If that's not the case, just edit the column index too accordingly.
Select this new helper column. Go to Data -> Filter Views... -> Create a new Filter. Select filter by value and check TRUE only.
Caveat: this filter can only work if there are actually rows with TRUE value. This will always be the case as some entries are always unique.
If you want to avoid the caveat in your future applications, you can use filter by conditions with custom formula. The formula b2 should work.
But to begin with, even without the helper column, the above formula should work. Why doesn't it? That would be a good question for Google Support should it exist.
You can also use a google apps script to do this. To open the script editor from google sheets:
Choose the menu Tools > Script Editor.
Copy and paste the following script:
function removeDuplicates() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var newData = [];
var ids = [];
for (var i in data) {
var row = data[i];
var duplicate = false;
if (ids.indexOf(row[0]) > -1) {
duplicate = true;
} else {
duplicate = false;
ids.push(row[0]);
}
if (!duplicate) {
newData.push(row);
}
}
sheet.clearContents();
sheet.getRange(1, 1, newData.length, newData[0].length).setValues(newData);
}
This assumes that you want to dedupe based on the contents of the first row of you sheet. If not you can adjust the row references from the 0 index to any other index you wish.
I would...
Create a new sheet
For column A, do =Unique(Sheet1!A:A)
Simply use VLOOKUP to populate the other columns. This will deliver the first value associated with the duplicates.
In my Google Form, I have custom formatting setup for my dropdown lists that will highlight the entire row based on the value chosen.
Custom formula is
=$B3="Highlight: 1 Row"
But how can I highlight multiple rows if I wanted to?
Google Sheet Example: Highlight Multiple Rows
Screenshot Example:
UPDATE (01-26-2017)
The purpose of this "multi row" feature will be used inside a more robust spreadsheet that generates Google Forms from a Google Sheet. For this particular use case, there are many different "dropdowns" for the user to choose from that control where the user can type. This can quickly become hard to navigate the longer your sheet becomes. The best way it seems to handle this is through conditional formatting.
Highlight Multiple Rows (Test Google Sheet)
Create Form From Sheet (YouTube Tutorial)
Create Form From Sheet (Google Sheet)
Create Form From Sheet (Google Script)
Use this custom formula
=$B$3="Highlight: 2 Row"
Make sure the range you want this rule to apply to, is two rows long
e.g: B3:Z4
Now when "Highlight: 2 Row" is entered in B3 two rows should be highlighted.
UPDATE:
Below is a google script that should work
function onEdit(e) {
var sh, colors, rows, rowsBefore;
sh = e.source.getActiveSheet();
colors = ["#fff2cc", "#fce5cd", "#d9ead3", "#cfe2f3"];
if (sh.getName() !== 'Example' || e.range.columnStart !== 2 || e.range.rowStart < 8 || typeof e.value == 'object') return;
rowsBefore = e.oldValue ? e.oldValue.replace(/\D/g, '') : 1;
rows = e.value.replace(/\D/g, '');
sh.getRange(e.range.rowStart, 4, rowsBefore, sh.getLastColumn()).setBackground("white")
sh.getRange(e.range.rowStart, 4, rows, sh.getLastColumn()).setBackground(colors[rows - 1])
}
Change sheet name (and range) to suit. Note that this script is on a simple onEdit trigger: it will be fired when an edit is done in col B of the sheet 'Example'. Do not try to run the script by clicking the 'play' button in the script editor as it will return an error. See if that helps ?