Google Script: adding up arrays - arrays

I want to write a google spreadsheet with two main parts: an individual order form to calculate price & a system to count the total orders.
I made a simplified version here: https://docs.google.com/spreadsheets/d/1_vRnX-qT3-0puYhtBCclYIVu504A-donE4nU8rQn41k/edit?usp=sharing
After noting an individual order and the order is paid, the green button is clicked. The button activates the payment-script.
The payment script needs to add the values of the individual order to the total orders and afterwards empty the individual orders.
function payment() {
var individualOrder = SpreadsheetApp.getActiveSheet().getRange('E3:E6').getValues();
var currentTotal = SpreadsheetApp.getActiveSheet().getRange('O3:O6').getValues();
var newTotal = [0,0,0,0];
for (var i = 0; i < 4; i++) {
newTotal[i] = currentTotal[i] + individualOrder[i];
}
SpreadsheetApp.getActiveSheet().getRange('O3:O6').setValues([newTotal]);
// Reset individual order after payment
SpreadsheetApp.getActiveSheet().getRange('E3:E6').setValue(0);
}
EDIT: Now it does work to replace the new Total values by using the code below, but there is another problem. When summing the array values 1 + 0, it cocatenates them to 10 instead of using math-values. Any ideas?
function payment() {
var individualOrder = SpreadsheetApp.getActiveSheet().getRange('E3:E6').getValues();
var currentTotal = SpreadsheetApp.getActiveSheet().getRange('O3:O6').getValues();
var newTotal = currentTotal;
Logger.log(individualOrder);
Logger.log(currentTotal);
for (var i = 0; i < 4; i++) {
newTotal[i] = [currentTotal[i] + individualOrder[i]];
}
Logger.log(newTotal);
SpreadsheetApp.getActiveSheet().getRange('O3:O6').setValues(newTotal);
// Reset individual order after payment
SpreadsheetApp.getActiveSheet().getRange('E3:E6').setValue(0);
}

If all you want to do is set 'individualOrder' to 0 try:
function payment() {
var individualOrder = SpreadsheetApp.getActiveSheet().getRange('E3:E6').getValues();
for (var i = 0; i <individualOrder.length ; i++) {
individualOrder[i]=[0];
}
SpreadsheetApp.getActiveSheet().getRange('E3:E6').setValues(individualOrder);
}

I solved it by making the sum in the spreadsheet and doing a .getValues on that range and using this in .setValues. A lot easier, only 2 lines of code.

Related

Is there a way to highlight duplicate combinations of values in two columns, regardless of the order?

I have a UFC database and I'm looking for rematches. Therefore, I need to find the duplicate combinations of names; the combinations that appear more than once. However, since the winner of the first fight could lose the rematch, I need to find duplicates regardless of the order in which they appear.
This is how my database is structured:
database example.
Fighter 1 is the winner and fighter 2 is the loser.
Here is a link to the database (got it from kaggle): https://docs.google.com/spreadsheets/d/19ISNhYFdGzgLZz1x4h2v_Q5Pq0cofw2rkUtwk3xPXGQ/edit?usp=sharing
Feel free to play around with it.
Any ideas on how to solve this?
Here is an example of the result I'd ideally want:
image example
Just a simple highlight over the duplicates.
In order to give you more precise help, it would be helpful if you could give an example of the result you want. As has been said, if there are many duplicates and you mark each group with a different colour, it can be visually confusing.
Anyway, I have written a couple of functions with Apps Script and Spreadsheet Service that you may find useful for your project.
Code 1
With these functions, you will be able to get all fights where two fighters that you previously define have participated:
function main() {
var name1 = 'Conor McGregor'
var name2 = 'Nate Diaz'
var result = findDuplicates(name1, name2)
console.log(result)
}
function findDuplicates(name1, name2) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Ark1')
var lastRow = sheet.getLastRow()
var lastCol = sheet.getLastColumn()
var range = sheet.getRange(1, 1, lastRow, lastCol).getValues()
var result = []
for (var i = 0; i < range.length; i++) {
if (range[i].includes(name1) && range[i].includes(name2)) {
result.push(range[i])
}
}
return result
}
Code 2
With this function, you can add in a third column, the same number for each pair of fighters. This way, it is very easy to change the "add a number" to "paint the cells in a colour".
function findPairs() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Ark2')
var lastRow = sheet.getLastRow()
var lastCol = sheet.getLastColumn()
var range = sheet.getRange(1, 1, lastRow, lastCol).getValues()
var indexToSkip = []
var k = 0
for (var i = 0; i < range.length; i++) {
if (!indexToSkip.includes(i)) {
var match = range[i]
indexToSkip.push(i)
sheet.getRange(i + 1, 3).setValue(k)
for (var j = i + 1; j < range.length; j++) {
if (range[j].includes(match[0]) && range[j].includes(match[1])) {
indexToSkip.push(j)
sheet.getRange(j + 1, 3).setValue(k)
}
}
k = k+1
}
}
}
References:
Apps Script
Spreadsheet Service

Google Sheets custom function displays "Loading..." forever in mobile app

I have written a custom function for Google Sheets in Apps Script. The goal is to have a sheet which automatically calculates who owes how much money to whom (e.g. to split a bill).
My sheet looks like this:
The first bill (Restaurant) is to be split among all 5 and the second bill is to be split among all 5 except Peter, because there is no 0 in B3.
The input for my Apps Script function will be cells B1 to F3 (thus, values AND names). The function works fine - it calculates the correct results. I open that spreadsheet via browser (sheets.google.com) AND via my phone app (Google Sheets). However, on my phone it often happens that the result cell (with the formula =calc_debt(B1:F3)) only displays "Loading ...". What's the problem?
For the sake of completeness, here is custom function's code:
function calc_debt(input) {
var credit = [0, 0, 0, 0, 0]; // credit[0] = Peter, credit[1] = Mark ...
for (var i = 1; i < input.length; i++) { // starting at i = 1 to skip the first row, which is the names!
// first: calculate how much everybody has to pay
var sum = 0;
var people = 0;
for (var j = 0; j <= 4; j++) {
if (input[i][j] !== "") {
sum += input[i][j];
people += 1;
}
}
var avg_payment = sum / people;
// second: calculate who has payed too much or too little
for (var j = 0; j <= 4; j++) {
if (input[i][j] !== "") {
credit[j] += input[i][j] - avg_payment;
}
}
}
// this function is needed later
function test_zero (value) {
return value < 0.00001;
};
var res = ""; // this variable will contain the result string, something like "Peter to Mark: 13,8 | Katy to ..."
while (!credit.every(test_zero)) {
var lowest = credit.indexOf(Math.min.apply(null, credit)); // find the person with the lowest credit balance (will be minus!)
var highest = credit.indexOf(Math.max.apply(null, credit)); // find the person with the highest credit balance (will be plus!)
var exchange = Math.min(Math.abs(credit[lowest]), Math.abs(credit[highest])); // find out by how much we can equalize these two against each other
credit[lowest] += exchange;
credit[highest] -= exchange;
res += input[0][lowest] + " to " + input[0][highest] + ": " + exchange.toFixed(2) + " | "; // input[0] = the row with the names.
}
return res;
}
I'm having a similar issue in the android app that loading a custom formula sometimes just shows 'Loading...', while in the web it always works fine. I've found a workaround to load the formulas in the android app:
Menu - > Export - > Save as - > PDF.
This will take a moment and behind the modal loading indicator you will see that the formulars eventually resolve. You can wait for the export to finish or cancel it as soon as you see your formular was resolved.
Also making the document available offline via the menu toggle could resolve the formulars.
Another thing you could do is using caching in your script. So whenever you use the web version to render more complex formulars the results are being stored and immediately loaded for the mobile app. Unfortunately, the Google cache is limited in time and does invalidate after a few hours. See here for more information:
https://developers.google.com/apps-script/reference/cache/
This two things work quite well. However, I'm searching for a better solution. Let me know if you find one.
Solved follow the solution provided here ..
Menu - > Export - > Save as - > PDF
This forces the script to run on mobile and be readable by the mobile sheet

Cannot call method "push" of undefined

I'm trying to make a spreadsheet to upload products in Prestashop by .csv. Everything works like a charm, but now I need to be able to make some changes in already entered products with the same spreadsheet. The only thing I came up with is to manipulate text strings from a cell into a variable array (after that I'll find a way to go forward).
Firstly I managed to combine about ~100 cells into one cell (which I accomplished with a complicated formula) but now I need the text from this cell to be separated and temporarily stored in an array variable.
This is the formula. It's joining columns in rows with a : separator and then joins those rows with a ; separator and in the end I just needed a number:
=regexreplace(regexreplace(regexreplace(concatenate(arrayformula(if($A$13:$A$50="","",if($C$13:$C$50="","",$A$13:$A$50&":"&$C$13:$C$50&if($D$13:$D$50="","",$D$13:$D$50)&":"&(ROW($A$13:$A$50)-12)&";"))))," :",":"),": ",":"),"\+","-")
This resulted in this text:
Producător:GARMIN:1;Tip:Ceas inteligent:3;Model:Vivomove HR
Premium:4;Culoare:Gold:5;Culoare curea:Light
brown:6;Greutate:56.5g:8;Rezolutie display:64x128:9;Tip
ecran:OLED:10;GPS:Da:15;Bluetooth:Da:16;Durata in regim de asteptare
(ore):168:24;Sensori:Heart RATE, Activity Tracker, Barometric
altimeter, Accelerometer, Smart notifications, Weather, Step counter,
Move bar, Calories burned, Floors climbed, Analog
hands:26;Garanție:24luni:38;
Now I need to separate everything back as it was, but by a code in Apps Script, so that I will be able to manipulate the values separately, but they would still be in a structured form.
It should be something like this (inside a two dimensional variable):
[0][0]Producător [0][1]GARMIN
[1][0]Tip [1][1]Ceas inteligent
[2][0]Model [2][1]Vivomove HR Premium
[3][0]Culoare [3][1]Gold
[4][0]Culoare curea [4][1]Light brown
[5][0]Greutate [5][1]56.5g
[6][0]Rezolutie display [6][1]64x128
[7][0]Tip ecran [7][1]OLED
[8][0]GPS [8][1]Da
[9][0]Bluetooth [9][1]Da
[10][0]Durata in regim de asteptare (ore) [10][1]168
[11][0]Sensori [11][1]Heart RATE, Activity Tracker, Barometric altimeter, Accelerometer, Smart notifications, Weather, Step counter, Move bar, Calories burned, Floors climbed, Analog hands
[12][0]Garanție [12][1]24luni
And now the main part... The next code is breaking with the error Cannot call method "push" of undefined
//var ss = SpreadsheetApp.getActiveSpreadsheet();
//var activeSheet = ss.getActiveSheet();
//var idSpreadsheet = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1zRsGMoXJzG9oht_pr3Rr24ksPqBzTZIvNcYPUKfphNI/edit#gid=1264546658").getSheetByName("RO").getDataRange().getValues();
//var idToChange = activeSheet.getRange("A12").getValue();
var row = 0;
//var userID = Session.getActiveUser();
var bufferFeatures = [{}];
bufferFeatures = idSpreadsheet[10][29];
var bufferImages = idSpreadsheet[row][27];
//var productRows = bufferFeatures.indexOf(";",0);
var testColumn = [];
var pos = 0; //here is where we start the text string
var del = 0; //here is where we find the ";" delimiter and stop slicing text string
// THIS FOR LOOP WORKS FINE
for (pos = 0; pos < bufferFeatures.length; pos = del) {
del = bufferFeatures.indexOf(";", pos);
testColumn.push(bufferFeatures.slice(pos, del));
del++;
};
var rownr = 0; //current row number.. not really using this variable
var pos1 = 0; //here is where we start the text string
var del1 = 0; //here is where we find the ":" delimiter and stop slicing text string
var columnsAndRows = [];
columnsAndRows.push([]);
var j = 0;
//THIS FOR LOOP GIVES ME TROUBLE
for (var x = 0; x <= testColumn.length; x++) {
for (pos1 = 0; pos1 + 1 < testColumn[x].length; pos1 = pos1) {
del1 = testColumn[j].indexOf(":", del1);
var theSlice = testColumn[j].slice(pos1, del1);
var theStop = testColumn[j].length;
//for some reason, I can't get this code to "push" j=2)
Logger.log("Adding " + theSlice);
columnsAndRows[j].push(theSlice);
del1++;
pos1 = del1;
Logger.log("Added")
}
Logger.log("Next row");
del1 = 0
j++;
//rownr++;
};
You need to declare columnsAndRows[j] as a array too.
columnsAndRows[j] = [];
Using split() would be much easier:
function strToArr(string) {
if (!string) {
var string = "Producător:GARMIN:1;Tip:Ceas inteligent:3;Model:Vivomove HR Premium:4;Culoare:Gold:5;Culoare curea:Light brown:6;Greutate:56.5g:8;Rezolutie display:64x128:9;Tip ecran:OLED:10;GPS:Da:15;Bluetooth:Da:16;Durata in regim de asteptare (ore):168:24;Sensori:Heart RATE, Activity Tracker, Barometric altimeter, Accelerometer, Smart notifications, Weather, Step counter, Move bar, Calories burned, Floors climbed, Analog hands:26;Garanție:24luni:38; "
}
var arr1 = string.substr(0, string.lastIndexOf(";")).split(';'); //split by ;
var arr2 = arr1.map(function(e) { return e.split(':').slice(0,2)}); //split each element of arr1 by : and return only the first two elements
Logger.log(arr2);
return arr2;
}

Split setValues() of array data into two sets of columns (e.g. B - H and AV - AX) - Google Spreadsheet

I have a for loop that builds an array of data of size n rows by 10 columns.
var dupRows = crossoverDuplicates.getDataRange();
var dupNumRows = dupRows.getNumRows();
var dupValues = dupRows.getValues();
var retreiveNotFound = [];
for (var c = 1; c < dupNumRows; c++) {
var dupRow = dupValues[c];
if (dupRow[10] == "") {
retreiveNotFound.push(dupValues[c]);
}
}
I then use setValues() to paste the array to the bottom of another sheet.
if (retreiveNotFound && retreiveNotFound.length) {
crossover.getRange(lastRowCrossoverData + 1, 2,
retreiveNotFound.length,10 ).setValues(retreiveNotFound);
}
However, the columns on the destination sheet have now moved and I need to paste the first 7 columns of the array in B - H and the last 3 in AV - AX.
Alternatively, I could create two arrays, one with the first 7 columns and another with the last 3 and then paste the separately.
Unfortunately, I can't figure out how to do either.
I'm guessing this has a relatively simple solution, but I'm just not searching for the right key words. Thanks in advance for your help!
Move Google spreadsheet columns into two separate places. Split Google spreadsheet columns up and put them into different columns or two different columns. This code gets data from one place, and then creates two separate arrays, and writes the data into two non-contiguous ranges.
function myFunction() {
var dupRows = crossoverDuplicates.getDataRange();
var dupNumRows = dupRows.getNumRows();
var dupValues = dupRows.getValues();
var B_H_Array = [];
var AV_AX_Array = [];
var AV_AX_TempArray, B_X_TempArray, c, dupRow, thisRowsData;//Define variables
//without assigning a value
for (c = 1; c < dupNumRows; c++) {
dupRow = dupValues[c];
if (dupRow[10] == "") {
thisRowsData = dupValues[c];
B_X_TempArray = thisRowsData.slice(0,7);//Slice out the first elements
AV_AX_TempArray = thisRowsData.slice(7,10);//Slice out last elements
B_H_Array.push(B_X_TempArray);
AV_AX_Array.push(AV_AX_TempArray);
}
}
if (B_H_Array.length) {
crossover.getRange(lastRowCrossoverData + 1, 2,B_H_Array.length, B_H_Array[0].length )
.setValues(B_H_Array);
}
if (AV_AX_Array.length) {
crossover
.getRange(lastRowCrossoverData + 1, 48,AV_AX_Array.length, AV_AX_Array[0].length )
.setValues(AV_AX_Array);
}
}

for loop not iterating over last element of 2D array in google app script (GAS)

I am working on a script that, in part, takes an array of names, compares each name to column A in a sheet, and returns with a row matched value in column B. (Like the vLookup command in sheets)
The setup
var ss = SpreadsheetApp.getActiveSpreadsheet();
var clientsSheet = ss.getSheetByName("Clients");
var cRow = clientsSheet.getLastRow();
var cColumn=clientsSheet.getLastColumn();
var cData=clientsSheet.getRange(1,1,cRow,cColumn).getValues(); //create array of client data
The trouble code
//put each client on their own row and add hour
for(i=0; i < client.length; ++i){
var cl = client[i]
//iterate over array of clients (column A) and hours (Column B) to find match and log the number in column B
for(j=0;j<cData.length;++j){
if (cData[j][0]==cl){
var hour = cData[j][1];
}
}
//return the matched values
Logger.log(cl+" - "+hour);
}
The var 'client' is an array that was split from a list of names in a single cell that are separated by commas (see whole code below)
At the moment it works great except that it misses the last element in the array.
for example:
if I have a sheet with two columns and three rows like so:
A 1
B 2
C 3
I would get back
A-1
B-2
C-
It is missing that last piece on the last element - it should be
A-1
B-2
C-3
I am stumped, and I know that it must be some simple little thing.
Any help would be amazing
Thanks!
The Code:
function logClients()
/*
Take data from a google form check box submissions. Check box submissions put all checked answers into a single cell separated by a comma. The function first takes the most recently submitted row, removes unneeded spaces, and splits each element into its own part of an array.
Then, the function compares each clients name in the array to a sheet with other info, such as the default number of hours we meet. It takes the clients name, the date of submission, and the hours, and logs them on a new row in two different sheets, the Hours sheet and the Trans Log sheet.
*/
var ss = SpreadsheetApp.getActiveSpreadsheet();
var logSheet = ss.getSheetByName("Log"); //Raw data from the Google form
var hourSheet = ss.getSheetByName("Hours"); //logged data for my records, separated into individual clients
var transLog = ss.getSheetByName("Trans Log"); // logged data minus "other" catagory
var clientsSheet = ss.getSheetByName("Clients"); //sheet containing all clients names and the typical hours we meet
var lRow = logSheet.getLastRow();
var hRow = hourSheet.getLastRow();
var tRow = transLog.getLastRow();
var cRow = clientsSheet.getLastRow();
var cColumn = clientsSheet.getLastColumn();
var cData = clientsSheet.getRange(1, 1, cRow, cColumn).getValues();
//get list of clients from cell and split it into an array
var Client = logSheet.getRange(lRow, 2).getValue().replace(", ", ","); //remove all spaces after a comma
var client = Client.split(",");
//get "other" information and do the same
var Other = logSheet.getRange(lRow, 5).getValue().replace(", ", ",");
var other = Other.split(",");
//check the date and set to today if nothing else has been entered
var dcell = logSheet.getRange(lRow, 4).getValue();
var date = new Date()
if (dcell == "") {} else if (dcell == "Yesterday") {
date = new Date(date.getTime() - 1 * (24 * 3600 * 1000));
} else {
date = dcell
}
var date = Utilities.formatDate(date, "GMT-8", "MM/dd/yy"); //format date
//put each client on their own row
for (i = 0; i < client.length; ++i) {
var cl = client[i]
var hour = logSheet.getRange(lRow, 3).getValue(); //hours
if (hour == !"") {
break;
}
for (j = 0; j < cData.length; ++j) {
if (cData[j][0] == cl) {
var hour = cData[j][1];
}
}
Logger.log(date + " - " + cl + " - " + hour);
hourSheet.appendRow([date,cl, hour]);
transLog.appendRow([date, cl, hour]);
}
//put each client on their own row
for (i = 0; i < other.length; i++) {
hourSheet.appendRow([date, other[i], getHour(client[i])]);
}
} //end of function
``
This is a code that I have been working on to teach myself Java and Apps-script
Yup, simple mistake.
Not all of the spaces before each name were being removed from the array, so the if statement would skip right over them because they were not a true match

Resources