Find cell address of each matching value in range - Google Apps Script - google-apps-script

I have a range that assigns shifts to a set of employees, in which the row labels are dates (ie, the Y axis is a chronological set of dates), and the column headers are locations (Building1, Building2, etc). Each row, then, contains employees assigned to each location for that day. Or, alternatively, each column will contain a chrono list of who will be assigned to the location specified in that column's header.
I am attempting to match a name, say "John Doe" for each instance he appears throughout the range, and return a 2 column list of dates and locations for which he is assigned. John Doe will be listed many times over the dates in question and various locations (in multiple columns).
I've reached the limit of my expertise both with AppsScript and Filter functions and greatly appreciate any help. I believe a loop is necessary, but perhaps there is a better way. For what its worth, my goal is to take this list and put every assignment on the user's calendar (I've solved for this already). TIA everyone!
Sample input and output situation

From your provided Spreadsheet, I believe your goal is as follows.
You want to achieve the following situation using Google Apps Script.
In this case, how about the following sample script?
Sample script:
Please copy and paste the following script to the script editor of Spreadsheet and save the script. When you use this script, please put a custom function of =SAMPLE(Data!A3:F20,"John Doe") to a cell. By this, the result values are returned.
const SAMPLE = ([h, ...v], searchName) =>
[["Data", "Location"], ...v.flatMap(([hh, ...vv]) => {
const i = vv.indexOf(searchName);
return i != -1 ? [[hh, h[i + 1]]] : [];
})];
If you don't want to include the header row, you can also use the following script.
const SAMPLE = ([h, ...v], searchName) =>
v.flatMap(([hh, ...vv]) => {
const i = vv.indexOf(searchName);
return i != -1 ? [[hh, h[i + 1]]] : [];
});
Testing:
When this sample script is used for your sample input values, the following situation is obtained.
In the case of "John Doe", from your expected output, "Building4" of "8/8/2022" is not included as shown in the red background cell. But, I'm worried that you might have miscopied. So, I proposed the above sample script. If you want to except for the value of the specific date, please tell me. This can be also achieved.
Reference:
Custom Functions in Google Sheets

The result that you are looking for could be achieved by using Google Sheets built-in functions in a formula:
=ARRAYFORMULA(QUERY(SPLIT(FLATTEN(Data!A4:A20&"💣"&Data!B3:F3&"💣"&Data!B4:F20),"💣"),"SELECT Col1,Col2 WHERE Col3 = 'John Doe'")
Briefly, the above formula uses FLATTEN and Google Sheets array handling feature to "unpivot" your double entry table, then uses QUERY to filter and limit the data to be returned.
Related
How do you create a "reverse pivot" in Google Sheets?

Related

Google Sheets | Search Range for substring in referenced cell and if found, then "Y"

I am having trouble finding a working formula to will search for the substring in a referenced cell ($A6 from Tracking 3) from the range ('AC 3'!$B4:B) and if it returns true, then a "Y" will show.
I currently have a google sheet that is used to track webinar attendance by searching the range (copied and pasted chat messages) for the referenced attendee same, and if the attendee's name is found, a "Y" is placed in that cell. The sheet is currently functional for our prior webinar platform, as when the chat messages are pasted, the attendee's name is isolated within its own cell, however, the new platform copies with the following format: "From 'attendee name' to All Panelist 00:00 PM". In the code I currently have, it is not recognized that the attendees' names are found within the ranges.
For a visual, I have "Y" input for the name Dylan Payne to show the end outcome I am looking for. I have tried the functions SEARCH, REGEXMATCH, and MATCH. I believe the SEARCH function is where my answer lies, but I'm having trouble reaching my desired outcome. If anyone is able to provide some additional feedback, it would be extremely appreciated.
I also use some google AppsScript, if there is a way to use that to isolate the attendees' names within the cell.
[Tracking 3]
[AC 3]
Add Y to range with searched for substrings
function fss() {
const ss = SpreadsheetApp.getActive():
const sh = ss.getSheetByName("AC3");
const sh0 = ss.getSheetByName("Tracking");
const sA = sh0.getRange(2,1,sh.getLastRow() - 1).getValues().flat();//names
const rg = sh.getRange("C4:N" + sh.getLastRow());//search range
sa.forEach((s,i) => {
rg.createTextFinder(s).matchEntireCell(false).findAll().forEach(r => {
r.setValue(r.getValue() + "Y")
});
});
}
I think I just found a useable code, actually:
=IF(ISTEXT(INDEX(FILTER('AC 3'!$B$4:$B,SEARCH($A20,'AC 3'!$B$4:$B)),1)),"Y","")
There is one more element I would like to implement from the previous code. In the previous code, it searched for the named range courseACs, and if the title ($A$1) is found in the name range, it would have a designated number of columns needed for that webinar duration (three columns per hour of duration).
Let's say the webinar title Ethics is two hours long and would need six columns for chat records. The formula then looks at the column and if it is in a column that resides after the sixth column for attentiveness tracking (Column K) then - will reflect in the cell. The original code is shown below:
=IF(IFERROR(E$5<=SEARCH(RIGHT($A$1,LEN($A$1)-15),courseACs,4),true),IF(OR(ISNUMBER(MATCH($A6,'AC 1'!C$4:C$2000,0))),"Y"," "),"-")

ImportXLM Using IfError on Google Sheets

I wanted to thank everyone for being so helpful on this site - it means a lot!
I am trying to import the likes/followers from a Spotify playlist to Google Sheets. It seems like various playlists have a different XPath.
I can extract a majority(most work) of the likes/followers using this code: (B24 is the URL)
=INDEX(REGEXEXTRACT(IFERROR(QUERY(ARRAY_CONSTRAIN(IMPORTDATA(B24), 500, 5), "select Col5 where Col4 contains 'followers'", 0), QUERY(ARRAY_CONSTRAIN(IMPORTDATA(B24), 500, 7), "select Col7 where Col6 contains 'followers'", 0)), "\d+")*1)
However, some playlist links come up with an empty output.
Example: https://open.spotify.com/playlist/5aSO2lT7sVPKut6F9L6IAc
Example of a working one: https://open.spotify.com/playlist/7qvQVDnLe4asawpZqYhKMQ
I'm honestly not sure how to add a third argument, and I have been blindly changing the col numbers to see what works - no luck. Any idea on how to figure out what col #'s to change to/any guidance would be extremely helpful.
Thank you!!
Issue and workaround:
When I saw the HTML from both URLs, I thought that in this case, the value, you want to retrieve, can be retrieved from the JSON data included in the HTML. But unfortunately, the JSON data is large. So when IMPORTXML is used, an error occurs because of the data size. So in this answer, I would like to propose to use a custom function using Google Apps Script.
Sample script:
Please copy and paste the following Google Apps Script to the script editor of Google Spreadsheet. And, please put =SAMPLE("###url###") to a cell. By this, the value of followers is returned.
function SAMPLE(url) {
const res = UrlFetchApp.fetch(url).getContentText();
const v = res.replace(/&/g, "&").match(/Spotify\.Entity \=([\s\S\w]+?);/);
return v && v.length == 2 ? JSON.parse(v[1].trim()).followers.total : "Value cannot be retrieved.";
}
Result:
When above script is used for your 2 URLs, the following result is obtained. In this case, the following custom formulas are put to the cells "A1" and "A2", respectively.
=SAMPLE("https://open.spotify.com/playlist/5aSO2lT7sVPKut6F9L6IAc")
=SAMPLE("https://open.spotify.com/playlist/7qvQVDnLe4asawpZqYhKMQ")
Note:
This sample script is for the URLs in your question. So when you tested it for other URLs, the script might not be able to used. And, when the structure of HTML is changed at the server side, the script might not be able to used. So please be careful this.
References:
Custom Functions in Google Sheets
fetch(url)

How Do You Find The Last Row In A Range That Matches Certain Criteria? (Google Apps Script)

I have a parts database of over 150 rows of data each with individually unique part numbers with starting letters to indicate their system name (Part-12XX are for bolts or whatnot). Is there a function like textfinder or intuitive way to find the last result of this text range criteria so I could automatically add a part with the next sequential number.
Excel uses find directions or even xlUp to search from bottom up. I looked up the class TextFinder and that did not seem to work.
I'm using Google Apps Script.
// Creates a text finder for the range.
var textFinder = range.createTextFinder('Part-12');
// string to be replaced with right() command
You want to retrieve the row number of the last row when the value which has Part-12 as the prefix is found in the range of the Spreadsheet.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this answer? Please think of this as just one of several answers.
Modification points:
In this answer, "TextFinder" is used.
useRegularExpression() is used. The official document is as follows.
configures the search to interpret the search string as a regular expression; otherwise the search interprets the search string as normal text.
findAll() returns all ranges of the search result as an array. In this case, the last element of the array is the value you want.
Sample script:
var textFinder = range.createTextFinder('^Part-12').useRegularExpression(true); // In this case, '^Part-12' is used at the regex.
var result = textFinder.findAll();
var row = result[result.length - 1].getRow();
Logger.log(row); // This is the last row number.
This script supposes that range is declared.
References:
useRegularExpression()
findAll()
If I misunderstood your question and this was not the result you want, I apologize.

How to create INDIRECT array string of multiple sheet references in Google Sheets?

I am attempting to use a query to display data off multiple Google Sheets. I make a new sheet every week that has a specific sheet name, e.g. Week of 01/13, Week of 01/06 and so forth.
The following is where my idea spawned from for reference:
I have a summary sheet that is using COUNTA(INDIRECT("'" & A5 &
"'!E4:E",true)
A5 being a cell that concatenates a date and words to replicate the
sheet names.
The row on the summary sheet does not populate until B5<=today()
So I am able to set it an forget it and the sheet will continue to
give me my weekly data as the days progress and keeps the sheets clean
until the week is upon us.
Long story short, I have a query that I use that gives me all the data I need with a specific parameter but I have to manually update the data syntax array with the new sheet names each week.
=QUERY({'Week of 01/13'!A:P;'Week of 01/06'!A:P;'Week of 12/30'!A:P;'Week of 12/23'!A:P;'WEEK OF 12/16'!A:P;'WEEK OF 12/09'!A:P;'WEEK OF 12/02'!A:P;'WEEK OF 11/25'!A:P;'WEEK OF 11/18'!A:P;'WEEK OF 11/11'!A:P;'WEEK OF 11/04'!A:P;'WEEK OF 10/28'!A:P;'WEEK OF 10/21'!A:P;'WEEK OF 10/14'!A:P;'WEEK OF 10/07'!A:P;'WEEK OF 09/30'!A:P;'WEEK OF 09/23'!A:P;'WEEK OF 09/16'!A:P;'WEEK OF 09/09'!A:P;'WEEK OF 09/02'!A:P},
"Select * where Col11 = 'RD' order by Col2 desc",0)
I would like to build a reference to an array that will auto-populate a concatenation based on the day.
Using the following code I can have the concatenate give me the array I need,
=if(H4<=today(),CONCATENATE("'",H$1,text(H4,"mm/dd"),"'!A:P;",),"")
but when I try to input it into the query function it just returns the concatenated text:
=QUERY(I1,"Select *")
'Week of 01/06'!A:P;'Week of 01/13'!A:P
I have tried with and without the curly brackets with no success.
I would like the sheet to be able to refresh and see that it is the correct day, the new sheet name is populated and the query gets updated.
I need help with making I1 work.
Link to Test Query Sheet
dudes who copy-pasted INDIRECT function into Google Sheets completely failed to understand the potential of it and therefore they made zero effort to improve upon it and cover the obvious logic which is crucial in this age of arrays.
in other words, INDIRECT can't intake more than one array:
=INDIRECT("Sheet1!A:B"; "Sheet2!A:B")
nor convert an arrayed string into active reference, which means that any attempt of concatenation is also futile:
=INDIRECT(MasterSheet!A1:A10)
————————————————————————————————————————————————————————————————————————————————————
=INDIRECT("{Sheet1!A:B; Sheet2!A:B}")
————————————————————————————————————————————————————————————————————————————————————
={INDIRECT("Sheet1!A:B"; "Sheet2!A:B")}
————————————————————————————————————————————————————————————————————————————————————
=INDIRECT("{INDIRECT("Sheet1!A:B"); INDIRECT("Sheet2!A:B")}")
the only possible way is to use INDIRECT for each end every range like:
={INDIRECT("Sheet1!A:B"); INDIRECT("Sheet2!A:B")}
which means that the best you can do is to pre-program your array like this if only part of the sheets/tabs is existant (let's have a scenario where only 2 sheets are created from a total of 4):
=QUERY(
{IFERROR(INDIRECT("Sheet1!A1:B5"), {"",""});
IFERROR(INDIRECT("Sheet2!A1:B5"), {"",""});
IFERROR(INDIRECT("Sheet3!A1:B5"), {"",""});
IFERROR(INDIRECT("Sheet4!A1:B5"), {"",""})},
"where Col1 is not null", 0)
so, even if sheet names are predictable (which not always are) to pre-program 100+ sheets like this would be painful (even if there are various sneaky ways how to write such formula under 30 seconds)
an alternative would be to use a script to convert string and inject it as the formula
A1 would be formula that treates a string that looks like real formula:
=ARRAYFORMULA("=QUERY({"&TEXTJOIN("; ", 1,
IF(A3:A<>"", "'Week of "&LEFT(A3:A, 5)&"'!A1:D5", ))&
"}, ""where Col1 is not null"", 1)")
further populating of A6:A will expand the string automatically
then this script will take the string from A1 cell and it will paste it as valid formula into C5 cell:
function onEdit() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Master Sheet');
var src = sheet.getRange("A1");
var str = src.getValue();
var cell = sheet.getRange("C5");
cell.setFormula(str);
}
of course, the script can be changed to onOpen trigger or with custom name triggered from the custom menu or via button (however it's not possible to use the custom function as formula directly)
If you're trying to update the data your query is looking at and you're feeding it a string, you need to put that string within the indirect() function. That will interpret your string as a data reference and point your query() in the right direction.
So for this you'd probably have
=QUERY(INDIRECT(I1),"Select *")

How query/search for information from one Google spreadsheet to another spreadsheet using GAS?

I would like to create a Google Apps Script to do the following: When I select a cell containing the name of a person in a spreadsheet (a spreadsheet that records sales, for example), and use a menu button to call the function, the script does a search (a query) using the person's name (or number of his document) in another spreadsheet that stores complete consumer data and that contains all the information that I need from that consumer to generate a contract or a payment receipt.
What is the best strategy to implement this search for information from one spreadsheet in another spreadsheet using Google Apps Script?
Do you have some script sample with a implementation similar to this? THANK YOU in advance for any help/guidance!
There is no event triggered by a cell selection, you'll have to use a menu item or a button to call the function or, if it is acceptable for your use case, edit the cell to trigger an onEdit event.
The 'search part' is actually very simple, the data being on the spreadsheet itself or in another one has no importance, it will simply change the way you access data ( getActiveSpreadsheet or openById()). From there just get all the values and do a search in the resulting 2D array.
EDIT following your comment : here is an example of such a code that returns the range of the found cell (and we get the A1 notation but we could getValue() as well of course.).
function test(){ // to test the find function with an argument, 'any' in this case
var result = findItem('any');
if(result){Logger.log(result.getA1Notation())}else{Logger.log('no luck !')};
}
function findItem(item){
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = ss.getDataRange().getValues()
for(var n = 0;n<data.length;++n){
if(data[n].indexOf(item)>-1){ // this is a "strict" find, ie the value must be the entire search item. If you want to do partial match you should compare differently...
return (ss.getRange(n+1,data[n].indexOf(item)+1)); // if found return the range. note the +1 because sheets have 1 index while arrays have 0 index
}
}
return false;// if we come to the end of sheet without result...
}