Google Sheets: delete rows containing specified data - button

I'm new to Java scripting and Google Apps Scripts so i am sorry if this has already been answered. I was not able to find what i was looking for over the last few months of working on this project.
I am working on a variant of the scripts here:
Delete row in Google Sheets if certain "word" is found in cell
AND
Google Sheet Script - Find Value in Col and Delete Row
I want to create a button, or menu, that will allow someone to enter specific data, and have each row in the spreadsheet containing that data deleted.
I have a test sheet here that illustrates the data i'm working with, formulas i'm using, and has the beginning of the script attached to it:
https://docs.google.com/spreadsheets/d/1e2ILQYf8MJD3mrmUeFQyET6lOLYEb-4coDTd52QBWtU/edit?usp=sharing
The first 4 sheets are pulling data from the "Form Responses 1" sheet via a formula in cell A:3 in each sheet so the data would only need to be deleted from the "Form Responses 1" sheet to clear it from the rest of the sheets.
I tried working this in but i do not think i am on the right track.
https://developers.google.com/apps-script/guides/dialogs
I also posted this on Google Docs Help Forum 60 days ago, but have not received any responses.
Any help would be greatly appreciated.

There's a few steps. For usability of UI this takes a little longer code. In concise form:
The user activates a dialog and enters a string.
Rows w/ the string are deleted (with error handling and confirmation)
(Hopefully this gets you started and you can tailor it to your needs)
Function that initiates the menu:
function onOpen(){
SpreadsheetApp.getUi()
.createMenu('My Menu')
.addItem('Delete Data', 'deleteFunction')
.addToUi();
}
The main workhorse:
function deleteFunction(){
//declarations
var sheetName = "Form Responses 1";
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName(sheetName);
var dataRange = sheet.getDataRange();
var numRows = dataRange.getNumRows();
var values = dataRange.getValues();
var delete_string = getUIstring();//open initial UI, save value
if (delete_string.length < 3) return shortStringError()//UI to protect your document from an accidental entry of a very short string.
//removing the rows (start with i=2, so don't delete header row.)
var rowsDeleted = 0;
for (var i = 2; i <= numRows; i++){
var rowValues = values[i-1].toString();//your sheet has various data types, script can be improved here to allow deleting dates, ect.
if (rowValues.indexOf(delete_string) > -1){
sheet.deleteRow(i - rowsDeleted);//keeps loop and sheet in sync
rowsDeleted++;
}
}
postUIconfirm(rowsDeleted);//Open confirmation UI
}
Isolated UI functions to help make above function more concise:
function getUIstring(){
var ui = SpreadsheetApp.getUi();
var response = ui.prompt("Enter the target data element for deletion")
return response.getResponseText()
}
function postUIconfirm(rowsDeleted){
var ui = SpreadsheetApp.getUi();
ui.alert("Operation complete. There were "+rowsDeleted+" rows deleted.")
}
function shortStringError(){
var ui = SpreadsheetApp.getUi();
ui.alert("The string is too short. Enter a longer string to prevent unexpected deletion")
}

I'll just show a way to delete the cell value if it matches your search criteria. It's up to you to connect it to buttons ,etc.
You'll loop through a Sheet Range. When you find the word match, delete it using clearContent()
function deleteSpecificData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range = sheet.getRange("Sheet1!A1:C4");
var values = range.getValues();
var numArray = [1,2,3,4,5,6,7,8,9];
var deleteItem = "Garen";
Logger.log(range);
for(var i=0; i< values.length; i++){
for(var j=0; j<values[i].length; j++){
if(values[i][j] == deleteItem){
var row = numArray[i];
var col = numArray[j];
var range = sheet.getRange(row,col).clearContent();
}
}
}
}
Before:
After:

Related

Remove date from Time in Google App Script

I am new to script and found a code that I have been tweaking for my needs but I keep running into a problem since I separated the date and time. My date field display and selected value match but the time fields keep defaulting to MM/DD/YY HH:MM A/P when selected. en I click on it (it displays correctly when not selected). I need to have the field only display the time and nothing else. so it will stop causing issues to my formulas on other sheets. I know my problem lies in the last part but so far what I have tried has failed.
//DEFINE ALL ACTIVE SHEETS
var ss = SpreadsheetApp.getActiveSpreadsheet();
//DEFINE MAIN SHEET
var mainSheet = ss.getSheetByName("MAIN");
//LAST ROW ON MAIN SHEET
var lastRow = mainSheet.getLastRow();
for (var j = 5; j <= lastRow; j++)
{
// CHECK CLOCK IN
if(mainSheet.getRange('b1:b1').getValue() == mainSheet.getRange(j, 1).getValue() && mainSheet.getRange(j,3).getValue() == '')
{
Browser.msgBox('Need to Clock Out before Clocking IN');
return;
}
}
// ADD CLOCK IN RECORD
mainSheet.getRange(lastRow + 1, 1).setValue(mainSheet.getRange('b1:b1').getValue()).setFontSize(12);
mainSheet.getRange(lastRow + 1, 2).setValue(new Date(new Date().setHours(0, 0, 0, 0))).setNumberFormat('MM/DD/YY').setHorizontalAlignment("center").setFontSize(12);
mainSheet.getRange(lastRow + 1, 3).setValue(new Date()).setNumberFormat("hh:mm A/P").setHorizontalAlignment("center").setFontSize(12);//````
You get a whole bunch of formatting options if you look for Utilities.formatDate
var date = Utilities.formatDate(new Date(), "Europe/Berlin", "dd.MM.yyyy")
var time = Utilities.formatDate(new Date(), "Europe/Berlin", "HH:mm")
Change "Europe/Berlin" to your location to get the current time. Now use the variables wherever you need them. Simple as that.
Pro tip: Try to use variables (just like shown above) for readability instead of fitting everything in one line.

IMPORTDATA not grabbing live data from XML

I'm using Google Sheet's IMPORTDATA function to grab information from an XML file that is pulling from an API but the information I pull into the sheet isn't up to date.
How can I modify my sheet to pull in up-to-date data?
Compare the sheet: https://docs.google.com/spreadsheets/d/1W0Bt5z-Tky-tNhG_JtfE4FfjTRgQNRu_eQu2qVhQ-_E/edit?usp=sharing (LiveScores sheet)
To the XML: https://www67.myfantasyleague.com/2019/export?TYPE=liveScoring&L=64741&APIKEY=&W=14&DETAILS=1&JSON=0
Observe franchise id="0015" in both sets of data.
The sheet states <franchise id="0005" score="0.00" gameSecondsRemaining="21600" playersYetToPlay="6" playersCurrentlyPlaying="0" isHome="0">
The XML states <franchise id="0015" score="11.14" gameSecondsRemaining="20004" playersYetToPlay="4" playersCurrentlyPlaying="2"> (This data is for a football game that is currently being played as I'm writing this so the above example may not be exact, but it WON'T be score of 0.00, for example.
Any help would be amazing, thanks!
Have you tried using IMPORTXML? Google Sheets IMPORTXML Page
In IMPORTXML, you can just use the Inspect Element feature to pull the xpath.
Hope this helps. Let me know if I can help further.
Edit: Instructions To Change When Data Is Imported
In the toolbar go to the script editor
Now in the scripts, paste the code listed bellow
/**
* Go through all sheets in a spreadsheet, identify and remove all spreadsheet
* import functions, then replace them a while later. This causes a "refresh"
* of the "import" functions. For periodic refresh of these formulas, set this
* function up as a time-based trigger.
*
* Caution: Formula changes made to the spreadsheet by other scripts or users
* during the refresh period COULD BE OVERWRITTEN.
*
* From: https://stackoverflow.com/a/33875957/1677912
*/
function RefreshImports() {
var lock = LockService.getScriptLock();
if (!lock.tryLock(5000)) return; // Wait up to 5s for previous refresh to end.
// At this point, we are holding the lock.
var id = "YOUR-SHEET-ID";
var ss = SpreadsheetApp.openById(id);
var sheets = ss.getSheets();
for (var sheetNum=0; sheetNum<sheets.length; sheetNum++) {
var sheet = sheets[sheetNum];
var dataRange = sheet.getDataRange();
var formulas = dataRange.getFormulas();
var tempFormulas = [];
for (var row=0; row<formulas.length; row++) {
for (col=0; col<formulas[0].length; col++) {
// Blank all formulas containing any "import" function
// See https://regex101.com/r/bE7fJ6/2
var re = /.*[^a-z0-9]import(?:xml|data|feed|html|range)\(.*/gi;
if (formulas[row][col].search(re) !== -1 ) {
tempFormulas.push({row:row+1,
col:col+1,
formula:formulas[row][col]});
sheet.getRange(row+1, col+1).setFormula("");
}
}
}
// After a pause, replace the import functions
Utilities.sleep(5000);
for (var i=0; i<tempFormulas.length; i++) {
var cell = tempFormulas[i];
sheet.getRange( cell.row, cell.col ).setFormula(cell.formula)
}
// Done refresh; release the lock.
lock.releaseLock();
}
}
This snippet of code came from Periodically refresh IMPORTXML() spreadsheet function
Last and definitely the least, replace the "YOUR-SHEET-ID"
NOTE: I have not personally tested this code and I cannot vouch for it. I recommend making a copy and testing it there first.
Hopefully, this solves the issue of your data not being imported as often as you want. If you want to manually get "fresh" data, you can just delete/cut the import function and paste it back in.
try in A2:
=ARRAYFORMULA(IFNA(VLOOKUP(C2:C, PlayerList!A:F, {2, 6}, 0)))
and in C2:
=ARRAYFORMULA(QUERY(REGEXEXTRACT(QUERY(IMPORTDATA(
"https://www67.myfantasyleague.com/2019/export?TYPE=liveScoring&L=64741&APIKEY=&W=14&DETAILS=1&JSON=0?273"),
"where Col1 contains 'player id'", 0),
"(player id=""(\d+)).+?(score=""(\d+.\d+))"),
"select Col2,Col4"))
spreadsheet demo

InDesign - Script to find specific words between angle brackets, and copy those to a list

Sorry in advance if I’m not phrasing this question correctly. I know nothing about InDesign scripting, but this would solve a workflow problem I’m having at my company, so I would appreciate any and all help.
I want to find all strings in an InDesign file that are between angle brackets (i.e. <variable>) and export that out into a list. Ideally this list would be a separate document but if it can just be dumped into a text frame or something that’s fine.
Any ideas on how to do this? Thank you in advance for any and all help.
Here is something simple:
app.findGrepPreferences=NothingEnum.NOTHING; //to reset the Grep search
app.findGrepPreferences.findWhat = '<[^<]+?>'; //the word(s) you are searching
var fnd = app.activeDocument.findGrep (); //execute search
var temp_str = ''; //initialize an empty string
for (var i = 0; i < fnd.length; i++) { //loop through results and store the results
temp_str += fnd[i].contents.toString() + '\r'; // add next found item text to string
}
var new_doc = app.documents.add (); //create a new document
app.scriptPreferences.measurementUnit = MeasurementUnits.POINTS; //set measurement units to points
var text_frame = new_doc.pages[0].textFrames.add({geometricBounds:[0,0,100,100]});// create a new text frame on the first page
text_frame.contents = temp_str; //output your text to the text frame in the new document
For more data see here.

More efficient way to add multiple records

So this works but it takes 15 seconds for a spreadsheet with 60 items.
function addToModel(name,birth,age){
var newRecord = app.models.ImportData.newRecord();
newRecord['PRESIDENT'] = name;
newRecord['BIRTH_PLACE'] = birth;
newRecord['AGE_ELECTED'] = age;
app.saveRecords([newRecord]);
}
function getSpreadsheet(){
var sh = SpreadsheetApp.openById("zzz");
var ss = sh.getSheetByName("Sheet1");
var data = ss.getDataRange().getValues();
THIS WAS WAY ONE, TAKES 15 SECONDS
for (var i=1; i<data.length;i++)
{
addToModel(data[i][1],data[i][2],data[i][3].toString());
}//for loop
}
but I noticed that the command is saveRecordS not saveRecord and with anything in google apps script, the fewer calls the better, so I tried this but it doesn't work
//SAME SPREADSHEET INFO
var result = [];
for (var i=0; i<data.length;i++)
{
var newRecord = app.models.ImportData.newRecord();
newRecord['PRESIDENT'] = data[i][1];
newRecord['BIRTH_PLACE'] = data[i][2];
newRecord['AGE_ELECTED'] = data[i][3].toString();
result.push(newRecord);
}//for loop
app.saveRecords([result]);
Expected result: new records in my table, much faster than the first version. Actual result: "Cannot read property "key" from undefined" which is triggered from the last line (saveRecords). I tried both app.saveRecords(result) and ([result]), same problem both times.
Note: this example is from an appmaker university tutorial that no longer works because of the changes for appmaker v2.
I think that it's model.newRecord() takes time for each new item created, while time on app.saveRecords() is ignorable.
Could you please confirm that you are EU user? as we EU user are facing same issue (link) due to the server location, if it is the case, please help star that issue and give more info to help Google solve that issue. Thanks.

How to create multi items/records or save item/record array at one time in client script file

I want to create multiple records at the same time using client script. This is what I'm doing:
var ceateDatasource = app.datasources.Reservation.modes.create;
var newItem = ceateDatasource.item;
newItem.User = user; //'eric'
newItem.Description = description; //'000'
newItem.Location_Lab_fk = lab.value.Id; //'T'
newItem.Area_fk = area.value.Id; //'L'
newItem.Equipment_fk = equipment.value.Id; //'S'
for(var i = 0 ; i < 3; i ++) {
newItem.Start_Date = startDate;
newItem.Start_Hours = '03';
newItem.Start_Minutes = '00';
newItem.End_Date = startDate;
newItem.End_Hours = '23';
newItem.End_Minutes = '30';
// Create the new item
ceateDatasource.createItem();
}
But the result I'm getting is this one:
The three records are created but the only the first one has data. The other two records have empty values on their fields. How can I achieve this?
Thanks.
Update(2019-3-27):
I was able to make it work by putting everything inside the for loop block. However, I have another question.
Is there any method like the below sample code?
var recordData = [Data1, Data2, Data3]
var ceateDatasource;
var newItem = new Array(recordData.length) ;
for(var i = 0 ; i < recordData.length; i ++) {
ceateDatasource = app.datasources.Reservation.modes.create;
newItem[i] = ceateDatasource.item;
newItem[i].User = recordData[i].user;
newItem[i].Description = recordData[i].Description;
newItem[i].Location_Lab_fk = recordData[i].Location_Lab_fk;
newItem[i].Area_fk = recordData[i].Area_fk;
newItem[i].Equipment_fk = recordData[i].Equipment_fk;
newItem[i].Start_Date = recordData[i].Start_Date;
newItem[i].Start_Hours = recordData[i].Start_Hours;
newItem[i].Start_Minutes = recordData[i].Start_Minutes;
newItem[i].End_Date = recordData[i].End_Date;
newItem[i].End_Hours = recordData[i].End_Hours;
newItem[i].End_Minutes = recordData[i].End_Minutes;
}
// Create the new item
ceateDatasource.createItem();
First, it prepares an array 'newItem' and only calls 'ceateDatasource.createItem()' one time to save all new records(or items).
I try to use this method, but it only saves the last record 'newItem[3]'.
I need to write a callback function in 'ceateDatasource.createItem()' but Google App Maker always show a warning "Don't make functions within a loop". So, are there any methods to call 'createItem()' one time to save several records? Or are there some functions like 'array.push' which can be used?
Thanks.
As per AppMaker's official documentation:
A create datasource is a datasource used to create items in a particular data source. Its item property is always populated by a draft item which can be bound to or set programmatically.
What you are trying to do is create three items off the same draft item. That why you see the result you get. If you want to create multiple items, you need to create a draft item for each one, hence all you need to do is put all your code inside the for loop.
for(var i = 0 ; i < 3; i ++) {
var ceateDatasource = app.datasources.Reservation.modes.create;
var newItem = ceateDatasource.item;
newItem.User = user; //'eric'
newItem.Description = description; //'000'
newItem.Location_Lab_fk = lab.value.Id; //'T'
newItem.Area_fk = area.value.Id; //'L'
newItem.Equipment_fk = equipment.value.Id; //'S'
newItem.Start_Date = startDate;
newItem.Start_Hours = '03';
newItem.Start_Minutes = '00';
newItem.End_Date = startDate;
newItem.End_Hours = '23';
newItem.End_Minutes = '30';
// Create the new item
ceateDatasource.createItem();
}
If you want to save several records at the same time using client script, then what you are looking for is the Manual Save Mode. So all you have to do is go to your model's datasource and click on the checkbox "Manual Save Mode".
Then use the same code as above. The only difference is that in order to persist the changes to the server, you need to explicitly save changes. So all you have to do is add the following after the for loop block:
app.datasources.Reservation.saveChanges(function(){
//TODO: Callback handler
});

Resources