Convert data from table to table - javascript

I was working in a project which is converting some data to another table by an online API (web service), so the plan is fetching data from the API and converting the prices to another table, I still got the same error again and again which is not convert each row as the way I need.
SCREENSHOT : THE UI TO UNDERSTAND THE USE
CODE :
function Send_Button() {
//activate the spreadsheet
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var cellCurrency = spreadsheet.getRange('E9').getValues();
//fatching the data from the API
var res = UrlFetchApp.fetch("https://api.exchangeratesapi.io/latest?base="+cellCurrency).getContentText();
//parsing data to JSON
var json = JSON.parse(res);
//exporting data in variables
var USD=json['rates']['USD'],
CAD=json['rates']['CAD'],
GBP=json['rates']['GBP'],
EUR=json['rates']['EUR'];
//an array to fetch just 4 currencies
var CRN = [USD,CAD,GBP,EUR];
var cellsPrice = spreadsheet.getRange('E5:E8').getValues();
//targeted cell in the second currencies table
var cellsTraget1 = spreadsheet.getRange('H3:K3');
var cellsTraget2 = spreadsheet.getRange('H4:K4');
var cellsTraget3 = spreadsheet.getRange('H5:K5');
var cellsTraget4 = spreadsheet.getRange('H6:K6');
//converting process
for(var i=0;i<4;i++)
{
cellsTraget1.setValue(cellsPrice[0]*CRN[0]);
cellsTraget2.setValue(cellsPrice[1]*CRN[1]);
cellsTraget3.setValue(cellsPrice[2]*CRN[2]);
cellsTraget4.setValue(cellsPrice[3]*CRN[3]);
}
}

You are iterating but not using the iterator variable i, is that correct? Perhaps writing the code in a more readable way with first getting the rows and then the cell value for each column and then iterating over both row and column would make it more readable and makes you find the issue faster.

Are you absolutely sure that your var cellCurrency is the correct range you want?
From your screenshot it appears the right cell is "E10"... but let's assume you fixed that.
Then you have an issue with your array dimensions, your var cellsPrice is a 2D array (Rows, Columns) even if the range you are getting values from is only one row!.
Another issue, your var cellsTraget* is also a 2D Range, so you can't use setValue()... You could use setValues() but the values array MUST match the range's dimension. And as they currently are, they don't match. Hint: transpose them if you want to use said function.
Apart from that, your for-loop makes no sense at all, since you are not looping anyhow!.
Here is some sample working code:
function sendButton() {
// Activate the spreadsheet
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var baseCurrency = "USD";
// Fetching the data from the API
var res = UrlFetchApp.fetch("https://api.exchangeratesapi.io/latest?base="+baseCurrency).getContentText();
// Parsing data to JSON
var json = JSON.parse(res);
// Exporting data in variables
var USD = [json['rates']['USD'], json['rates']['CAD'], json['rates']['GBP'], json['rates']['EUR']];
// I will do it for one row
var articlePrice = spreadsheet.getRange('E5').getValue();
// Mind the array's dimensions!
var convertedPricesRange = spreadsheet.getRange('I5:L5');
var convertedPriceValues = [[articlePrice*USD[0],articlePrice*USD[1],articlePrice*USD[2],articlePrice*USD[3]]];
// Set converted values
convertedPricesRange.setValues(convertedPriceValues);
}
Further reading:
Sheets Class Range

Related

how to change value in a range of cell to a data array in google AppScript?

How to pull data in a range of cell and then push those values into an array of data?
In this code I tried to get those values in range E2:E97 and then push those values to an array such as [E2 value, E3 Value, etc] then set value to database sheet using dataS.getRange(dataS.getLastRow()+1).setValue(value);
but it seems I can't get it done with those code. so any idea to do this?
// Save Data
function saveData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("newform");
var dataS = ss.getSheetByName("newdb");
var dataBaseNameColIndex = 1;
var formDataRange = ['E2:E97'];
var ui = SpreadsheetApp.getUi();
var response = ui.alert(
'Save Data ?',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (response == ui.Button.YES) {
for (var i = 2; i < formDataRange.length; i++) {
var value = formS.getRange(formDataRange[i], 5).getValue();
dataS.getRange(dataS.getLastRow() + 1).setValue(value);
console.log(saveData);
}
}
}
Instead of trying to copy and paste data cell-by-cell, you may want to do it in one go. That would be more efficient.
So instead of:
var value = formS.getRange(formDataRange[i], 5).getValue();
you could use the following without the for loop:
var allValues = formS.getRange('E2:E97').getValues();
And then post the data to the new range like this:
dataS.getRange(dataS.getLastRow() + 1, 2, allValues.length, allValues[0].length).setValues(value);
I am assuming you want to paste the data into column 2 onwards. Adjust the 2nd value in getRange() above accordingly.
The 3rd and 4th values in getRange() above are the number of rows and columns to pasts.
allValues.length is the number of rows of data. In this case it would be 95.
and allValues[0].length would the number of columns in the top row of the data copied. And in this case it should be one.
I am suggesting this way as you won't need to keep fiddling with the number of rows and columns if you change the copy range dimensions later on.
PS:
dataS.getRange(dataS.getLastRow() + 1).setValue(value);
is missing the column number in getRange()

How to retrieve a particular row from an array in google apps script?

I am using google sheets quite a lot, but now I am trying to use google apps script to get and update dynamic data retrieved from formulas into a static table.
So, I have a sheet called 'dynamique', with formulas retrieving, filtering and sorting data from other spreadsheets.
I want to be able to work on this data, so I am trying to create a button which would copy all the values from the 'dynamique' sheet into another sheet called 'statique'. That is, I want a formula which would check if the values from the column C of the 'dynamique' sheet are in the column C of the 'statique' sheet. And if the values aren't there, I want the script to copy them. (columns A and B are empty)
I've managed to get my script to work for one column, but now, I want to copy the whole line.
For example, if the value in dynamique!C10 can't be found in statique!C:C, my script writes the value of dynamique!C10 in the first empty cell of the column statique!C:C. But I want it to write dynamique!C10:J10 into my destination sheet (say it's going to be maybe statique!C8:J8).
Here is my code, working for only one cell.
function dynamicToStatic() {
var dynSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("dynamique");
var staSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("statique");
var dynLength = dynSheet.getRange("C1:C").getValues().filter(String).length;//.getLastRow();
var staLength = staSheet.getRange("C1:C").getValues().filter(String).length;
var staRange = staSheet.getRange(6,3,staLength-1);
var staValues = staRange.getValues();
var rangeToCheck = dynSheet.getRange(6,3,dynLength-1,8);
var valuesToCheck = rangeToCheck.getValues();
var numRows = rangeToCheck.getNumRows();
var staNumRows = staRange.getNumRows();
for (i = 0; i<= numRows; i++) {
var row = valuesToCheck[i];
var index = ArrayLib.indexOf(staValues , -1 , row);
if (index == -1) {
//if (staValues.indexOf(row) != -1) {
staSheet.getRange(i+6,3,1,8).setValues(row);
}
}
var timestamp = new Date();
staSheet.getRange(4,3).setValue('List updated on the: '+timestamp);
}
Now I can't manage to retrieve the whole line of the array, so as to be able to copy it using range.setValues(). I always get error messages.
Any help would be more than appreciated...
function gettingFullRows() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheetByName('Sheet1');
const shsr=2;//data startrow
const vA=sh.getRange(shsr,1,sh.getLastRow()-shsr+1,sh.getLastColumn()).getValues();
let html='';
vA.forEach((r,i)=>{
html+=Utilities.formatString('<br />Row:%s is %s',i+shsr,r.join(','));
});
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html), "Row");
}
So i did some re-writing to your code and made some comments in there. I hope this will make some things clear.
Array's are 0 indexed. So if the value is NOT found in the .indexOf then it would return -1. Also (for speed) i first push all the result to a array and then set the array in one "action" this saves a lot of time. The calls to and from a sheet takes the most time.
For the conversion to a 1d array i used spread operator
See this link for difference in const / var / let
The timestamp string i updated with the use of Template literals
If you have some questions, shoot! (also i did not test this ofcourse)
function dynamicToStatic() {
const dynSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("dynamique");
const staSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("statique");
const dynValues = dynSheet.getRange(1,3,dynSheet.getLastRow(),8).getValues();
//This is a 2d array
const staRaw = staSheet.getRange(6, 3, staSheet.getLastRow()).getValues();
//Convert to 1d array, for the indexoff later on, this way it is easier.
const staValues = [].concat(...staRaw);
//to store the output, as a 2d array, inside the if you see i push it in as array so you have the 2d array for the setValues.
const output = [];
for (let i = 0; i < dynValues.length; i++){
//i = the index of the array (row) inside the array of rows, the 0 would be the values of column C.
if (staValues.indexOf(dynValues[i][0]) >= 0){
output.push([dynValues[i]]);
}
}
//Start by the lastrow + 1, column C(3), ouput is a array of arrays(rows), then get the [0].lengt for the columns inside the row array.
staSheet.getRange(staSheet.getLastRow()+1, 3, output.length, output[0].lenght).setValues(output);
const timestamp = new Date();
staSheet.getRange(4,3).setValue(`List updated on the: ${timestamp}`);
}

How do I insert an array into a Google Doc using data from Google Sheets?

I am trying to pull a range of names from a Google sheet and place it into a Google Doc.In the spreadsheet, the last names("lastNames") come before the first names ("firstNames"), and both are in separate columns. I am trying to place the first and last names together into my doc with the first names first.
I used a for loop to put the first and last names together into an array ("fullNames"), and that part works just fine. When I used Logger.log, all the first names and last names are together in an array, with each full name separated by a common, just the way I wanted them to be.
What I can't figure out how to do is actually insert this new array into the body of the document. I am using the appendTable method, but every time I try to I get the following error: "The parameters (number[]) don't match the method signature for DocumentApp.Body.appendTable."
What changes do I have to make to my code to actually place my new array into my google doc?
function namePusher() {
var ss = SpreadsheetApp.openById("1CHvnejDrrb9W5txeXVMXxBoVjLpvWSi40ehZkGZYjaY");
var lastNames = ss.getSheetByName("Campbell").getRange(2, 2, 18).getValues();
var firstNames = ss.getSheetByName("Campbell").getRange(2, 3, 18).getValues();
//Logger.log(firstNames);
var fullNames = [];
for(var i = 0; i < firstNames.length; i++){
var nameConcat = firstNames[i] + " " + lastNames[i]
fullNames.push(nameConcat);
}
//Logger.log(fullNames);
var doc = DocumentApp.getActiveDocument().getBody();
doc.appendTable(fullNames);
}
Modification points:
I think that there 2 reasons in your issue.
Values retrieved by getValues() is 2 dimensional array.
data of appendTable(data) is required to be 2 dimensional array.
In your script, fullNames is 1 dimensional array. By this, such error occurs.
In your script, the values are retrieved 2 columns using 2 getValues(). In this case, the cost will become a bit high. You can retrieve the values using one getValues().
When these points are reflected to your script, it becomes as follows.
Modified script:
function namePusher() {
var ss = SpreadsheetApp.openById("1CHvnejDrrb9W5txeXVMXxBoVjLpvWSi40ehZkGZYjaY");
var values = ss.getSheetByName("Campbell").getRange("B2:C19").getValues(); // Modified
var fullNames = [];
for(var i = 0; i < values.length; i++){ // Modified
var nameConcat = [values[i][1] + " " + values[i][0]]; // Modified
fullNames.push(nameConcat);
}
var doc = DocumentApp.getActiveDocument().getBody();
doc.appendTable(fullNames);
}
References:
getValues()
appendTable(cells)
One simple way to fix your code is by replacing
fullNames.push(nameConcat);
by
fullNames.push([nameConcat]);
The problem with your script is that fullNames is an Array of strings but your should pass an Array of Arrays of strings (or objects that might be coerced to strings).
Basic demo
var data = [
['A','B','C'],
[1, 'Apple','Red'],
[2, 'Banana','Yellow']
];
function myFunction() {
const doc = DocumentApp.getActiveDocument();
const body = doc.getBody();
body.appendTable(data);
}
As mentioned on Tanaike's answer there are other "improvement opportunities"
Reduce the number of calls to the Google Apps Script Classes and Methods
Use better ways to manage Arrays and to concatenate strings.

excel javascript API array handling

I'm working on an add-in for excel 2016 using the javascript API. I can successfully get the range into an array and get the values to show in console.log. I've also been able to get the values into a JSON array using JSON.stringify();
I need to manipulate the array to remove the empty values ("").
Can this be accomplished by using regular javascript array methods?
I'm thinking I can display the results back into a different worksheet using a similar approach like i did with var shWk
Here are some snippets of what I'm trying to do:
(function () {
"use strict";
// The initialize function must be run each time a new page is loaded
Office.initialize = function (reason) {
$(document).ready(function () {
app.initialize();
//document.getElementById("date").innerHTML = Date("MAR 30 2017");
$('#deleteTab').click(deleteTab);
$('#preview').click(preview);
$('#publish').click(publish);
});
};
function preview() {
Excel.run(function(ctx) {
//getting the colname from a date range in B2
var colName = ctx.workbook.worksheets.getItem('preview').getRange("B2");
colName.load('values');
return ctx.sync().then(function() {
//converting colname value to string for column name
var wkN = (colName.values).toString();
// displaying on the task pane
document.getElementById("tst").innerText = wkN;
// testing to confirm i got the correct colname
var shWk = ctx.workbook.worksheets.getItem('preview').getRange("B3");
shWk.values = colName.values;
//building the column connection by setting the table name located on a different worksheet
var tblName = 'PILOT_ZMRP1';
var tblWK = ctx.workbook.tables.getItem(tblName).columns.getItem(wkN);
//loading up tblWK
tblWK.load('values');
return ctx.sync().then(function(){
//this is where my question is:
var arry = tblWK.values;
for (var i=0; i < tblWK.length; i++){
if (tblWK.values !== ""){
arry.values[i][0]) = tblWK.values[i][0]
};
};
console.log(arry.length); //returns 185
console.log (arry.values);//returns undefined
tblWK.values = arry;
var tblWeek = tblWK.values;
console.log(tblWeek.length);//returns 185
console.log(tblWK.values);//returns [object Array] [Array[1],Array[2]
})
});
}).catch(function (error) {
console.log(error);
console.log("debug info: " + JSON.stringify(error.debugInfo));
});
}
What am I missing? Can you point me to some resources for javascript array handling in the specific context of office.js?
I want to thank everyone for the time spent looking at this question. This is my second question ever posted on Stack Overflow. I see that the question was not written as clear as it could've been. What i was trying to achieve was filtering out the values in a 1D array that had "". The data populating the array was from a column in a separate worksheet that had empty values (hence the "") and numeric values in it. the code below resolved my issue.
//using .filter()
var itm = tblWK.values;
function filt(itm){
return itm != "";
}
var arry = [];
var sht = [];
var j=0;
var s=0;
arry.values = tblWK.values.filter(filt);
//then to build the display range to show the values:
for (var i=0; i < itm.length-1; i++) {
if (tblWK.values[i][0]){
var arry; //tblWK.values.splice(i,0); -splice did not work, maybe my syntax was wrong?
console.log("this printed: "+tblWK.values[i][0]);
var cl = ('D'+i); //building the range for display
j++; //increasing the range
s=1;//setting the beignning range
var cll = cl.toString();//getRange() must be a string
console.log(cll);//testing the output
}
}
//using the variable from the for loop
var cl = ('D'+s+':D'+j);
var cll = cl.toString();
console.log(cll);//testing the build string
sht = ctx.workbook.worksheets.getItem('Preview').getRange(cll);
sht.values = arry.values; //displays on the preview tab
console.log (arry.values); //testing the output
The question was probably easier said by asking what vanilla javascript functions does office.js support. I found a lot help reading Building Office Add-ins using Office.js by Micheal Zlatkovsky and by reading the MDN documentation as well as the suggested answer posted here.
Regards,
J
I'm not sure what this check is trying to achieve: tblWK.values !== "". .values is a 2D array and won't ever be "".
For Excel, the value "" means that the cell is empty. In other words, if you want to clear a cell, you assign to "". null value assignment results in no-op.
You can just fetch the values form the array that contains null by using for each and can can push the null values into another array.

Google apps script - using for loop to pull data from range based on condition

I'm having an issue pulling the correct values out of a for loop in Google Sheets.
Here's my code:
Note: this is a snippet from a larger function
function sendEmails() {
var trackOriginSheet = SpreadsheetApp.getActiveSpreadsheet().getName();
var getMirSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Miranda");
//Set a new object to hold conditional data
var holdingData = new Object();
//Create function to get values from origin sheet
var returnedValues = function (trackOriginSheet) {
//Load dynamic variables into an object via returnedValues()
if (trackOriginSheet === getMirSheet) {
var startMirRow = 2; // First row of data to process
var numRowsMir = 506; // Number of rows to process
// Fetch the range of cells A2:Z506
var dataRangeMir = getMirSheet.getRange(startMirRow, 1, numRowsMir, 26);
// Fetch values for each cell in the Range.
var dataMir = dataRangeMir.getValues();
for (var k in dataMir) {
var secondRowMir = dataMir[k];
var intRefDescMir = secondRowMir[3];
var intAdminActionsMir = secondRowMir[4];
//Push returned data to holdingData Object
holdingData.selectedData = secondRowMir;
holdingData.refDesc = intRefDescMir;
holdingData.adminActions = intAdminActionsMir;
}
}
}
Here's a copy of the sheet I'm working on
What I need to have happened here first, is track the origin sheet, then create an object to hold data returned from the returnedValues() function. Later, I'll call the properties of this object into a send email function.
The problem is that I need to be able to pull data from the selected sheet dynamically (the "Miranda" sheet in this case.) In other words, when a user selects the "Yes" option in column I of the Miranda sheet, the first thing this script needs to do is pull the values of the variables at the top of the for loop within the same row that the user selected "Yes." Then, I'm pushing that data to a custom object to be called later.
It's apparent to me, that I'm doing it wrong. There's, at least, something wrong with my loop. What have I done? :)
EDIT:
After reviewing the suggestion by VyTautas, here's my attempt at a working loop:
for (var k = 0; k < dataMir.length; k++) {
var mirColI = dataMir[k][8];
var mirRefDesc = dataMir[k][2];
var mirAdminActions = dataMir[k][3];
var mirDates = dataMir[k][4];
if (mirColI === "Yes") {
var activeRowMir = mirColI.getActiveSelection.getRowIndex();
//Pull selected values from the active row when Yes is selected
var mirRefDescRange = getMirSheet.getRange(activeRowMir, mirRefDesc);
var mirRefDescValues = mirRefDescRange.getValues();
var mirAdminActionsRange = getMirSheet.getRange(activeRowMir, mirAdminActions);
var mirAdminActionsValues = mirAdminActionsRange.getValues();
var mirDatesRange = getMirSheet.getRange(activeRowMir, mirDates);
var mirDatesValues = mirAdminActionsRange.getValues();
var mirHoldingArray = [mirRefDescValues, mirAdminActionsValues, mirDatesValues];
//Push mirHoldingArray values to holdingData
holdingData.refDesc = mirHoldingArray[0];
holdingData.adminActions = mirHoldingArray[1];
holdingData.dates = mirHoldingArray[2];
}
}
Where did all that whitespace go in the actual script editor? :D
You already correctly use .getValues() to pull the entire table into an array. What you need to do now is have a for loop go through dataMir[k][8] and simply fetch the data if dataMir[k][8] === 'Yes'. I also feel that it's not quite necessary to use for (var k in dataMir) as for (var k = 0; k < dataMir.length; k++) is a lot cleaner and you have a for loop that guarantees control (though that's probably more a preference thing).
You can also reduce the number of variables you use by having
holdingData.selectedData = mirData[k]
holdingData.refDesc = mirData[k][2] //I assume you want the 3rd column for this variable, not the 4th
holdingData.adminActions = mirData[k][3] //same as above
remember, that the array starts with 0, so if you mirData[k][0] is column A, mirData[k][1] is column B and so on.
EDIT: what you wrote in your edits seems like doubling down on the code. You already have the data, but you are trying to pull it again and some variables you use should give you an error. I will cut the code from the if, although I don't really see why you need to both get the active sheet and sheet by name. If you know the name will be constant, then just always get the correct sheet by name (or index) thus eliminating the possibility of working with the wrong sheet.
var titleMirRows = 1; // First row of data to process
var numRowsMir = getMirSheet.getLastRow(); // Number of rows to process
// Fetch the range of cells A2:Z506
var dataRangeMir = getMirSheet.getRange(titleMirRows + 1, 1, numRowsMir - titleMirRows, 26); // might need adjusting but now it will only get as many rows as there is data, you can do the same for columns too
// Fetch values for each cell in the Range.
var dataMir = dataRangeMir.getValues();
for (var k = 0; k < dataMir.length; k++) {
if (dataMir[k][7] === 'Yes') { //I assume you meant column i
holdingData.refDesc = dataMir[k] //this will store the entire row
holdingData.adminActions = dataMir[k][3] //this stores column D
holdingData.dates = dataMir[k][4] //stores column E
}
}
Double check if the columns I have added to those variables are what you want. As I understood the object stores the entire row array, the value in column called Administrative Actions and the value in column Dates/Periods if Applicable. If not please adjust accordingly, but as you can see, we minimize the work we do with the sheet itself by simply manipulating the entire data array. Always make as few calls to Google Services as possible.

Categories