How to Query ALL ever-expanding sheets without naming each one - google-sheets

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;
}

Related

Is there a way to easily sum a few columns together, but only if the text next to them matches a dropdown selection

I've been trying to figure out what i'm doing wrong here when i'm doing the sumif formula's in b2,c2,d2
I have a lot going on, I realize. The data we are looking at, is between L5:U21
I have a query in a5 that pulls from l5:U that pairs any data in n5:n,p5:p,r5:r,t5:t to the selected data in the dropdown in a2. This part is working correctly for what I need.
B2 I am trying to extract from the top 3 options in the range b5:J that match a2, and add them together. Ultimately I'd like to do this if they do not have "Left" or "Right" in the J column as well.
To achieve this I pulled the data from b5:I into a sortn function seen in y5.
=SORTN(B5:I,3,,B5:B,false,D5:D,false,F5:F,false,H5:H,false)
and then my SUMIF function is as follows: =SUMIF(Z5:AF,A2,Y5:AE)
C2 is similar to B2, but I only data that matches the selection in a2, but also have "Left" in the J column.
I tried to achieve this with a similar SUMIF function i'm using in b2, but it seems to only pull the left most cell's data in the range given, not the matching column's data. So lets say if e9 = example1, it doesn't then grab the matching 2 in d9, it grabs whatever is in b9 only, and adds that. Which right now, it adds them all. I want to ultimately only pull the top 1, but I cannot even get it working correctly with all of them.
=SUMIF(J5:J,"Left",B5:H)
D2 is the same as C2, but "Right" in the J column.
This is my example / testing document I created to get a closer look at what's going on, if what i'm explaining isn't making a ton of sense.
https://docs.google.com/spreadsheets/d/1eZ7_yOrkoy_PCgcn_YxscPnDCvLXWK48JW-S7DqEgdQ/edit?usp=sharing
Try the following in C2
=QUERY({{B5:C;D5:E;F5:G},{J5:J;J5:J;J5:J}},
"select sum(Col1) where Col2='"&A2&"' and Col3='Left'
label sum(Col1) '' ",0)
For cell D2 all you need to do is use Col3='Right'
=QUERY({{B5:C;D5:E;F5:G},{J5:J;J5:J;J5:J}},
"select sum(Col1) where Col2='"&A2&"' and Col3='Right'
label sum(Col1) '' ",0)
In case you want to add more ranges like columns H-I you would adjust your formula like:
{{B5:C;D5:E;F5:G;H5:I},{J5:J;J5:J;J5:J;J5:J}}
(Do adjust the formula according to your ranges and locale)
SUGGESTION
I was experimenting earlier with SUMIFS & QUERY formulas to no success, as I also have a limited knowledge when it comes to implementing advanced Google Sheet formulas in a complex scenario. What I can suggest you try is by using a Custom Function formula in Google Sheet made possible by Google Apps Script that's integrated to the Google Sheet service.
This custom formula function will filter the range that does not contain Left or Right in column J based on the selected drop-down data, and then it returns the top 3 results in descending order.
The Custom formula Script named as CUSTOM_FUNC
/**
* Filters data in descending order from a range that doesn't contain any Left & Right based on selection in a cell dropdown selection & return the sum of the top 3 result.
*
* #param {B5:J21,A2} reference The range to be used.
* #returns The range and the cell reference to used in filtereing the data.
* #customfunction
*/
function CUSTOM_FUNC(data,dropdown_selection) {
/**Filter data that do not contain Left or Right on column J */
var lvl1 = data.map(x => {return x.toString().includes('Left') || x.toString().includes('Right') ? null : x }).filter(y => y)
/**Further filter lvl1 that matches the drop down selection*/
var res = lvl1.map(d => {
return d.map((find, index) => {return find == dropdown_selection ? d[index-1] : null}).filter(z => z)
});
/**Return the top 3 result in descending order */
return res.sort().reverse().slice(0, 3);
}
The parameters of this customer formula would be:
=CUSTOM_FUNC(data,dropdown_selection)
data
The sheet range (e.g. B5:J21) where the data you'd like to be processed resides.
dropdown_selection
The cell reference of the drop-down selection on your spreadsheet file.
To add this script in your spreadsheet file, copy and paste the script as a bound script in your Spreadsheet file by following this official guide
Demonstration
You can use the custom function formula named CUSTOM_FUNC similarly to how you use another Google Sheet formula on a cell, like this =SUM(CUSTOM_FUNC(B5:J21,A2))

Is there a way to use FILTER with multiple Sheets containing a specific phrase in Google Sheets?

I will try to be as clear as possible. Here is the example piece: link
What I want to happen is that the Filter formula will search for any Sheet containing “Form Responses” and then display the results. You can see on the Current sheet how I’ve been doing, but this is more tedious and leads to issues of the first formula begins to overwrite the next one, etc. On the Wanted tab, I’ve laid out how I imagine it and put a note in A7. Any help offered is greatly appreciated!
You can get started with this script:
function getSheetResponses(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Wanted");
var getCellValue = sheet.getRange("A7").getValue(); //Gets the name of your designated sheet on "Wanted Sheet" cell "A7"
var getName = ss.getSheetByName(getCellValue);
var getNameValue = getName.getRange(2,5,getName.getLastRow(),1).getValues(); //Gets all the values of Column E on any defined sheet names based on getCellValue
var datalength = getNameValue.length;
Logger.log(datalength);
sheet.getRange(8,6,datalength,1).setValues(getNameValue); //Puts the data on Wanted Sheet Column F
}
What this does is it gets the sheet name on cell A7, and populates the data on Column F row 8 on the "Wanted" sheet like so:
Now, the data it populates on the "Wanted" sheet came from Form Responses 1 based on the sample piece you have provided:
If ever you would want to relocate which specific row or column the data would be pasted on "Wanted" Sheet. You can refer to this documentation on how to modify the rows and columns on sheet.getRange()
Reference:
https://developers.google.com/apps-script/reference/spreadsheet/sheet#getrangerow,-column,-numrows,-numcolumns

How to extract specific text from a text string when data is in another worksheet?

I have a column containing 60 Text string entries in google sheet. These are job posts.
one of the entry look like this.
"Procurement Manager (MEP & Civil) urgently (Construction experience is Mandatory).Company - Kleindienst experience - Experience with 5-star hotels & luxury project will be preferred, min 10 years’ experience with same level position."
in another worksheet in google sheet i have a column containing more than 2000 job titles.
Say Sheet2!A1:A2000 .
I want if there is any job titles which is in another google work sheet is present in above text string then show that job title in next coulmn.
I used following formula of vlookup but failed to extract job title. sheet name in this case is Data Lookup , A2:A2000 contains all job titles in Data lookup sheet where 9 column contains same jobs titles for lookup value. C2 contains jobs descriptions as mentioned above.
=VLOOKUP(C2,'Data Lookup'!A2:A2000,9,FALSE)
but this formula is giving error. how can i get job titles.
You might want to consider using this custom function to achieve your goal.
Sample Code:
/** *
* #customfunction
*/
function findmatch(text, list){
var result = 'Not Found';
list = list.flat();
list.forEach( str => {
if(text.includes(str)){
result = str;
return result;
}
});
return result;
}
What it does?
provide the text string in the first parameter then the list of job titles on the 2nd parameter
Change 2-d array to 1-d array using Array.flat()
Loop all job titles and check if the text string contains the current job title using String.includes()
Return the matched job title.
Output:
Formula: =findMatch(A2,$C$2:$C$10)
Note: I used $ to lock the job title range so that it wont adjust while you drag your formula.
(UPDATE)
Here is an example if your job titles' are found on another sheet Data Lookup
Formula: =findMatch(A2,'Data Lookup'!$A$2:$A$10)
Here is an example if your job titles' are found on another Google Sheet
Pre-requisite:
Use Importrange() formula first to give access to the Google Sheet being imported.
Importrange Formula: =IMPORTRANGE("Your sheet url","$A$2:$A$10")
Formula: =findMatch(A2,importrange("Your sheet url","$A$2:$A$10"))
try:
=VLOOKUP(C2; 'Data Lookup'!I:J; 2; 0)
it translates as: look for C2 in column I and if match is found return column J

How can I concatenate text to a filtered column in Google Sheets?

I'm working with google forms and google sheets. I'm trying to create a summary sheet that will automatically update as the form is being filled.
I've been able to pull the data from the other sheets using a FILTER function. Now I want to add a column that shows the name of a country to the filtered column. I tried using concatenate but it didn't work as well as I'd hoped. Can someone help me figure out how to solve this problem.
Please see here for an example of the problem.
Well this is a very inelegant brute force way, but I think it works. See Solution-GK in your sheet.
=QUERY({
{TRANSPOSE(SPLIT(REPT("Nigeria~",ROWS(UNIQUE(FILTER(NIGERIA!A:E,NIGERIA!C:C<TODAY(),NIGERIA!B:B="Charity Fundraiser")))),"~")),
UNIQUE(FILTER(NIGERIA!A:E,NIGERIA!C:C<TODAY(),NIGERIA!B:B="Charity Fundraiser"))};
{TRANSPOSE(SPLIT(REPT("Sierra Leone~",ROWS(UNIQUE(FILTER('SIERRA LEONE'!A:E,'SIERRA LEONE'!C:C<TODAY(),'SIERRA LEONE'!B:B="Charity Fundraiser")))),"~")),
UNIQUE(FILTER('SIERRA LEONE'!A:E,'SIERRA LEONE'!C:C<TODAY(),'SIERRA LEONE'!B:B="Charity Fundraiser"))}},
"select Col1,Col2,Col3,Col4, Col5 where Col2 is not null")
I've added a hard coded literal of the country name, repeated it the number of times needed for the matching data rows, and made it into the first column in your existing data array. I repeat this for the second array you have for the second country.
I'm sure there are far more elegant ways to do this, so we'll see what else is proposed. If you had a list somewhere in your sheet of your country names - ie. Nigeria and Sierra Leone, possibly many more - I'm sure an elegant solution would cycle through those names, pulling the name to build the concatenated data ranges, and also adding the name as the text for each row.
Without needing a list in the sheet, a little bit of code could find all of your tab names, and exclude the non data ones, eg. Solution Here and Summary, and process all of the rest as data.
Note: I'm not clear that you need your UNIQUE statements, unless you are expecting duplicates for some reason. Also, the outer QUERY doesn't seem to be necessary - the inner FILTERs seem to do everything you need.
You could do this with an Apps Script Custom Function.
First, open a bound script by selecting Tools > Script editor, and copy the following functions to the script (check inline comments for more details about the code):
// Copyright 2020 Google LLC.
// SPDX-License-Identifier: Apache-2.0
function SUMMARIZE_FUNDRAISING_EVENTS(sheetNames, ...ranges) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
sheetNames = sheetNames.split(","); // Comma-separated string to array of sheet names
const filteredData = sheetNames.map(sheetName => { // Iterate through each sheet name
const sheet = ss.getSheetByName(sheetName);
if (sheet) { // Check if sheet exists with this name
const sheetData = sheet.getRange(2,1,sheet.getLastRow()-1,4).getValues(); // Source sheet data
const filteredData = sheetData.filter(rowData => {
return rowData[1] === "Charity Fundraiser" && rowData[2] < new Date()
}); // Filter data according to date and type of event
filteredData.forEach(filteredRow => filteredRow.unshift(sheetName)); // Add sheet name to filtered data
return filteredData;
}
}).flat();
return filteredData;
}
Once it is defined, you can use the function SUMMARIZE_FUNDRAISING_EVENTS the same you would any sheets built-in function. This function would accept a series of parameters:
A comma-separated string with the names of the sheets whose data should be summarized (don't add blank spaces after the comma or similar).
The different source ranges (in your case, NIGERIA!A:E and 'SIERRA LEONE'!A:E).
Both of these are necessary, because, on the one side, specifying the source ranges as parameters makes sure that the function executes and updates the summarized data every time the source ranges are edited, and on the other side, when passed as parameters, these source ranges don't contain information about the sheet names, which the script will need when returning the summarized data.
Example of calling the function:
Reference:
Custom Functions in Google Sheets

Google Sheets - Pushing a row to a second spreadsheet based on a value being entered

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.

Resources