Compare objects in array to rows on sheet - arrays

first time posting on here so be gentle! Haha.
I'm more than happy to post my current code if my explanation doesn't meet everyones standards.
I have an array which I have ripped data from an API stored in it. Each object in the array contains 3 values;
User name
User ID
Current statistic
Each row on my sheet contains these 3 properties and I want to list the last 30 days of value 3.
So on the first day, a row may contain
A1 "John" B1 "12345" C1 "5"
The 2nd day it would contain
A1 "John" B1 "12345" C1 "20" D1 "5"
3rd day
A1 "John" B1 "12345" C1 "40" D1 "20" E1 "5"
I can do this even with my limited knowledge of loops, what I have issues with is if there is a new entry in the array that is NOT on the sheet I need to be able to add it. Then I can continue adding data every day from there.
Thanks in advance.
function GymStrength1() {
var ss = SpreadsheetApp.openById("1bXc_AZcAAl09bf0ibfC6vQSY9O3ikpn0Ru7pZ4oZ0g8");
var sheet = ss.getSheetByName("Strength");
var currentData = sheet.getDataRange().getValues();
var response = UrlFetchApp.fetch("https://api.torn.com/faction/?selections=basic,contributors&stat=gymstrength&key=NhNhNbCmgGaqx0vQ");
var json = response.getContentText();
var data = JSON.parse(json);
var totalMembers = data["contributors"]["gymstrength"];
var activeMembers = [];
var newData = [];
// Gets active members and their daily contribution
for (var obj in totalMembers){
var playerID = obj;
var currentStat = data["contributors"]["gymstrength"][obj]["contributed"];
var currentMember = data["contributors"]["gymstrength"][obj]["in_faction"];
if (currentMember == "1"){
activeMembers.push([data["members"][obj]["name"],playerID,currentStat])
}
}
// for each active member, check against sheet first
// if row contains member, then merge row with member and paste into newData
// if row doesnt contain member then add member into newData
var copy = false;
for (var member in activeMembers){
for (var i = 1; i < currentData.length; i++){
var row = currentData[i];
// updates each row. works fine
if (activeMembers[member][1] == row[2]){
newData.push([activeMembers[member][0],activeMembers[member][1],activeMembers[member][2],row[3],row[4]]);
copy = true;
}
}
if (!copy){
newData.push([activeMembers[member][0],activeMembers[member][1],activeMembers[member][2],"0","0"]);
}
}
console.log(activeMembers)
console.log(newData)
sheet.getRange(2, 2, currentData.length, currentData[0].length).clearContent();
sheet.getRange(2, 2, newData.length, newData[0].length).setValues(newData);
}
I'm basing the comparison of the newData array and the sheet on the currentStat value of each entry. If the currentStat of an object in activeMembers is NOT on the sheet, I want the name, playerID and currentStat pushed to the newData array. If the currentStat IS on the sheet, it pushes the name, playerID and currentStat to the newData array as well as copying the rest of the values on that row. I can do that.
I am having trouble with adding entries that are NOT on the sheet, updating them works fine.
Brief example:
activeMembers array has obj name, playerID, currentStat
[
['John', '2856', '50'],
['Bob', '2957', '20'],
['Peter', '4579', '80']
]
sheet currently only has
[['John', '2856', '40']]
I would like the end result on the sheet to be
John 2856 50 40
Bob 2957 20
Peter 4579 80
John's 40 value has shifted to the right and the current value put in its place.

Create a map of sheet data: id=>oldStat
Modify the activeMembers array in place using the map data
/*<ignore>*/console.config({maximize:true,timeStamps:false,autoScroll:false});/*</ignore>*/
const activeMembers = [
['John', '2856', '50'],
['Bob', '2957', '20'],
['Peter', '4579', '80'],
];
const sheetData = [['John', '2856', '40']];
const sMap = new Map();
sheetData.forEach(([, id, oldStat]) => sMap.set(id, oldStat));
activeMembers.forEach(row =>
row.push(sMap.has(row[1]) ? sMap.get(row[1]) : '')
);
console.info({sMap,activeMembers});
console.log(JSON.stringify(activeMembers));
<!-- https://meta.stackoverflow.com/a/375985/ --> <script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script>

Related

GAS Filter Criteria to Filter records in an Array that have NON-Blank (text) cells in selected single or multiple fields

I'm trying to find a way in Apps Script, using an array method (NOT using a table), to filter records from a large array that have text data in selected fields.
The records in the newly filtered array would contain ONLY those that have any (text) data in the specified field(s).
The filtered data array would then be copied into a 'Sheets' table for further use.
Finding records with blank fields using "" as the criteria, or specific data, such as "Yes", works well.
My work-around involves a 'for' loop to clear records from the table into which the array has been copied. However, this takes more time than than the 'filter' records method.
The example shows the method to filter only records that have a 'Blank' cell in the specified field. I have tried so many possible criteria that I've lost track of every option I have tried, but here are some of the criteria I've tried to find records that have non-blank cells: "<>" !="" !"" "!Null" ">0" "is not null" "!Empty".
var Ss = SpreadsheetApp.getActiveSpreadsheet();
var DataSheet = Ss.getSheetByName("VolunteerListTbl");//Source Table of data
var LstRowNum = DataSheet.getLastRow();
var LstColNum = DataSheet.getLastColumn();
// "DataSheetRangeValues" is an array of entire dataset
var DataSheetRangeValues = DataSheet.getRange(3,1, LstRowNum , LstColNum).getValues();
var FilterCriteria = ""; //CASE: NO Coordinator assigned
var FilteredData = DataSheetRangeValues.filter(function(e){return e[1]===FilterCriteria});
var NewSheetName = "CustomSearch_Tbl"; //Each search gets a different 'Sheet' name
var C_SrchResSheet = Ss.getSheetByName(NewSheetName);
//Copy data from 'FilteredData' array to a new table 'NewSheetName'
C_SrchResSheet.getRange(3,1,FilteredData.length,LstColNum).setValues(FilteredData);
RecordsFound = FilteredData.length;
Try this:
function myfunk() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("VolunteerListTbl");
const lr = sh.getLastRow();
const lc = sh.getLastColumn();
const vs = sh.getRange(3, 1, lr - 2, lc).getValues();//note the numrows param
const fvs = vs.filter(e => e[1] != "");//this does not seem consistent with the description in your question
const nsh = ss.getSheetByName("CustomSearch_Tbl");
nsh.getRange(3, 1, fvs.length, fvs[0].length).setValues(fvs);
}

Is there a way to filter an array for strings in google apps script?

I am trying to filter the array 'employee_name' consisting of NaNs and one string element, to exclude any element BUT the string. The context is that I have a spreadsheet containing employee's birth dates, and I'm sending an email notification in case there's a birthday two days from today. My variables look like this:
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Employees');
var range = ss.getRange(2, 1, ss.getLastRow()-1, 1); // column containing the birth dates
var birthdates = range.getValues(); // get the `values` of birth date column
var today = new Date ();
var today = new Date(today.getTime());
var secondDate = new Date(today.getTime() + 48 * 60 * 60 * 1000);
var employee_name = new Array(birthdates.length-1);
And the loop:
for (var i=0;i<=birthdates.length-1;i=i+1){
var fDate = new Date(birthdates[i][0]);
if (fDate.getDate() == secondDate.getDate() &&
fDate.getMonth() == secondDate.getMonth()){
//define variables for outgoing email
for (var j=0; j<=birthdates.length-1;j=j+1){
employee_name[j] = [NaN];
}
employee_name[i] = ss.getRange(i+2,6);
employee_name[i] = employee_name[i].getValues();
}
}
after which the array in question looks like this
Logger.log(employee_name);
[[[Mia-Angelica]], [NaN], [NaN], [NaN], ..., [NaN]]
I have already tried the filter(Boolean), but this isn't working:
employee_name_filtered = employee_name.filter(Boolean);
Logger.log(employee_name_filtered);
returns [[[Mia-Angelica]], [NaN], [NaN], [NaN], ..., [NaN]].
I have also tried filling the non-string array entries with numeric values (instead of NaN) and then apply
employee_name_filtered = employee_name.filter(isFinite);
Logger.log(employee_name_filtered);
returns [[1.0], [2.0], [3.0], ..., [72.0]], so this filter method is working, but then I would need the 'inverse' of that because I want to keep the string.
I need the array within array to store the values at the position of the counter variable where the condition's met (similar to How to store data in Array using For loop in Google apps script - pass array by value).
This is my first time posting a question on SO, so if I overlooked any 'rules' about posting, just let me know and I will provide additional info.
Any help will be appreciated!
EDIT:
what I would like to receive in the end is simply
[[Mia-Angelica]].
The array you are using a 2 dimensional array - meaning it's an array of arrays so the filter method you are using cannot be applied in the same manner.
For this, I suggest you try the below snippet.
function cleanArray() {
var initialArray = [
['Mia-Angelica'],
['Space'],
['2'],
[NaN],
[NaN],
[NaN],
[NaN]
];
var finalArray = [];
for (let i = 0; i < initialArray.length; i++) {
var midArray = initialArray[i].filter(item => (Number.isFinite(item) && item.id !== 0) || !Object.is(item, NaN));
finalArray.push(midArray);
}
console.log(finalArray.filter(item => item != ''));
}
Note
Please bear in mind that getValues will return an Object[][] which is a two-dimensional array of values.
Reference
Apps Script Range Class;
Array.prototype.filter().

Google Apps Script: how to create an array of values for a given value by reading from a two column list?

I have a set of data in a Google spreadsheet in two columns. One column is a list of article titles and the other is the ID of a hotel that is in that article. Call it list1.
Example data
I would like returned a new list with article titles in one column, and an array of the hotel IDs in that article in the other column. Call it list2.
Example data
There are thousands of lines that this needs to be done for, and so my hope was to use Google Apps Script to help perform this task. My original thinking was to
Create column 1 of list2 which has the unique article titles (no script here, just the G-sheets =unique() formula.
Iterate through the titles in list2, looking for a match in first column of the list1
If there is a match:
retrieve its corresponding value in column 2
push it to an empty array in column two of list2
move onto next row in list1
if no longer a match, loop back to step 2.
I've written the following code. I am currently getting a type error (TypeError: Cannot read property '0' of undefined (line 13, file "Code")), however, I wanted to ask whether this is even a valid approach to the problem?
function getHotelIds() {
var outputSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('list2');
var lastRow = outputSheet.getLastRow();
var data = outputSheet.getRange(2,1,lastRow,2).getValues();
var workingSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('list1');
var lastActiveRow = workingSheet.getLastRow();
var itemIDS = [];
for (var i=1; i<=data.length; i++) {
var currentArticle = data[i][0];
var lookupArticle = workingSheet[i][0];
if (currentArticle === lookupArticle) {
var tempValue = [workingSheet[i][1]];
itemIDS.push(tempValue);
}
}
}
Use a simple google sheets formula:
You can use a very simple formula to achieve your goal instead of using long and complicated scripts.
Use =unique(list1!A2:A) in cell A2 of list2 sheet to get the unique hotels.
and then use this formula to all the unique hotels by dragging it down in column B.
=JOIN(",",filter(list1!B:B,list1!A:A=A2))
You got the idea right, but the logic needed some tweaking. The "undefined" error is caused by the workingSheet[i][0]. WorkingSheet is a Sheet object, not an array of data. Also, is not necessary to get the data from list2 (output), it is rather the opposite. You have to get the data from the list1 (source) sheet instead, and iterate over it.
I added a new variable, oldHotel, which will be used to compare each line with the current hotel. If it's different, it means we have reached a different Hotel and the data should be written in list2.
function getHotelIds() {
var outputSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('list2');
var outLastRow = outputSheet.getLastRow();
var workingSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('list1');
var lastActiveRow = workingSheet.getLastRow();
var sourceValues = workingSheet.getRange("A2:B" + lastActiveRow).getValues();
var itemIDS = [];
var oldHotel = sourceValues[0][0]; //first hotel of the list
for (var i = 0; i < sourceValues.length; i++) {
if (sourceValues[i][0] == oldHotel) {
itemIDS.push(sourceValues[i][1]);
/*When we reach the end of the list, the oldHotel variable will never be different. So the next if condition is needed. Otherwise it wouldn't write down the last Hotel.
*/
if (i == sourceValues.length - 1) {
outputSheet.getRange(outLastRow + 1, 1, 1, 2).setValues([
[sourceValues[i][0], itemIDS.toString()]
]);
}
} else {
outputSheet.getRange(outLastRow + 1, 1, 1, 2).setValues([
[sourceValues[i - 1][0], itemIDS.toString()]
]);
oldHotel = sourceValues[i][0]; //new Hotel will be compared
outLastRow = outputSheet.getLastRow(); //lastrow has updated
itemIDS = []; //clears the array to include the next codes
}
}
}
I also converted the itemIDS array to a String each time, so it's written down in a single cell without issues.
Make sure each column of the Sheet is set to "Plain text" from Format > Number > Plain Text
References
getRange
setValues
toString()

Search for Row based on Column Value, Change Values of Row Found from Values in Input sheet- Google Apps Script (Sheets)

I'm new to JavaScript... I have 2 spreadsheets created. 1 that has input values, and the 2nd is a target sheet where I want to update information.
I am searching through the target sheet with values from the input sheet with a column labeled "OpportunityID". Once the associated row is found, I want to update the target row with the values from the input sheet.
I've been able to search the target sheet with OpportunityID value from the input sheet and pull the values of columns in that specific row, but I am having trouble changing the values on the target sheet to the corresponding values on the input sheet.
Here is the code I have tried so far that pulls the appropriate information, but I need help resetting the values of that row:
function updateOpportunity() {
// Get active spreadsheets and sheets
var inputSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Search & Create New Records');
var OppsAndContracts = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Opportunities & Contracts');
var opportunityUpdateCopy = inputSheet.getRange('A8:Q8').getValues();
Logger.log(opportunityUpdateCopy);
//Search for Opportunities using OpportunityID
var last=OppsAndContracts.getLastRow();
var data=OppsAndContracts.getRange(1,1,last,17).getValues();// create an array of data from columns A through Q
var opportunityID = updateSheet.getRange("A8").getValue();
Logger.log(opportunityID);
for(nn=0;nn<data.length;++nn){
if (data[nn][0]==opportunityID){
var OppID = (((data[nn][0])));
var OppName = ((data[nn][1]));
var AccountID = ((data[nn][2]));
var AccountName = ((data[nn][3]));
var OppOwner = ((data[nn][4]));
var CloseDate = ((data[nn][5]));
var Amount = ((data[nn][6]));
var ProposalOwner = ((data[nn][7]));
var Stage = ((data[nn][8]));
var AeroServicesProducts = ((data[nn][9]));
var MechServicesProducts = ((data[nn][10]));
var ProjectStatus = ((data[nn][11]));
var PaymentIssues = ((data[nn][12]));
var UniquePaymentTerms = ((data[nn][13]));
var PaymentTerms = ((data[nn][14]));
var ProposalNumber = ((data[nn][15]));
var ContractNumber = ((data[nn][16]));} ;
OppsAndContracts.getRange([data]).setValues(opportunityUpdateCopy);
}
I've also tried getting the cell reference of the cells in the row with the corresponding OpportunityID and setting them with the values from the input sheet, but that hasn't worked either.
Any help or advice is much appreciated!
You need to use the nn value to select the range when there's a match, since nn is effectively your row index.
function updateOpportunity() {
// Get active spreadsheets and sheets
var ss = SpreadsheetApp.getActiveSpreadsheet();
var updateSheet = ss.getSheetByName("Update Sheet"); // You didn't have this defined, so I added
var inputSheet = ss.getSheetByName('Search & Create New Records');
var OppsAndContracts = ss.getSheetByName('Opportunities & Contracts');
var opportunityUpdateCopy = inputSheet.getRange('A8:Q8').getValues();
//Search for Opportunities using OpportunityID
var last = OppsAndContracts.getLastRow();
var data = OppsAndContracts.getRange(1,1,last,17).getValues(); // create an array of data from columns A through Q
var opportunityID = updateSheet.getRange("A8").getValue();
for (var nn = 0; nn < data.length; ++nn) {
if (data[nn][0] == opportunityID) {
OppsAndContracts.getRange(nn + 1, 1, 1, 17).setValues(opportunityUpdateCopy);
}
}
}
(I removed a bunch of the code that was irrelevant to your question.)

Append row based on value in another cell

Using a Google App Script I'm looking to find any ID's from Sheet2 which exist in Sheet1 and append the comment field within Sheet1 with what is listed in the Comment field in Sheet2.
Sheet1: Holds all data based on ID
Sheet2: Holds comments relating to some ID's in Sheet1
Sheet1 Example
ID Type In Stock Comment
1 Apple Yes
2 Banana No
3 Orange Yes
Sheet2 Example
ID Comment
1 Text
2 Text
Code
This is code I've been using for something else which loops through my source data to identify a variable called "Yes", obviously this won't work for this case as the ID is the variable we need to find which is dynamic.
I'm just a bit lost on how to modify this code so that it will loop through Sheet2, get all the IDs, check those IDs against Sheet1. If those IDs exist in Sheet1 update the comment of Sheet1 with the comment already listed in Sheet2
function setComment(){
var outputSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var lastRow = outputSheet.getLastRow();
var lastCol = outputSheet.getLastColumn();
var targetValues = [];
var sourceSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2");
var lastSourceRow = sourceSheet.getLastRow();
var lastSourceCol = sourceSheet.getLastColumn();
var sourceRange = sourceSheet.getRange(1, 1, lastSourceRow, lastSourceCol);
var sourceData = sourceRange.getValues();
{
//Loop through every retrieved row from the Source
for (row in sourceData) {
//IF Column I in this row has 'Yes', then work on it.
if (sourceData[row][1] === 'Yes') {
//Save it ta a temporary variable
var tempvalue = [sourceData[row][0], sourceData[row][7]];
//then push that into the variables which holds all the new values to be returned
targetValues.push(tempvalue);
}
}
//Save the new range to the appropriate sheet starting at the last empty row
outputSheet.getRange(lastRow + 1, 1 , targetValues.length, 2).setValues(targetValues);
}
}
Yes you need to loop through one group of IDs. In that loop you need to nest a loop that goes through the other group of IDs.
Code
function setComments() {
var ss = SpreadsheetApp.getActive(),
compare1 = "", compare2 = "",
outputSheet = ss.getSheetByName("Sheet1"),
sourceSheet = ss.getSheetByName("Sheet2"),
range1 = outputSheet.getDataRange(),
range2 = sourceSheet.getDataRange(),
lastCol1 = range1.getNumColumns(),
lastCol2 = range2.getNumColumns(),
values1 = range1.getValues(),
values2 = range2.getValues(),
// get the range of the titles
titleSection1 = outputSheet.getRange(1,1,1, lastCol1),
titleSection2 = sourceSheet.getRange(1,1,1, lastCol2),
// get the values from the titles
titles1 = titleSection1.getValues(),
titles2 = titleSection2.getValues(),
// get the column # for "ID" and "comment"
idCol1 = titles1[0].indexOf("ID"),
idCol2 = titles2[0].indexOf("ID"),
commentsCol1 = titles1[0].indexOf("comment"),
commentsCol2 = titles2[0].indexOf("comment");
// get the IDs from range1
for (i = 1; i < values1.length; i++) {
compare1 = values1[i][idCol1];
// get the IDs from range2
for (j = 1; j< values2.length; j++){
compare2 = values2[j][idCol2];
// if same ID, change the values array
if (compare1 == compare2) {
values1[i][commentsCol1] = values2[j][commentsCol2];
}
}
}
// set values based on the values array
range1.setValues(values1);
}

Resources