I'm using Google Scripts with the aim of calling an already filled cell using the ID. Once the data has been called, the user would be able to update and send it back to the main "data", in case some update is necessary.
The code isn't giving any error, but is not updating the cells either. See below an example of the code:
function Update() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("App"); // From sheet
var dataS = ss.getSheetByName("Results"); // Get the data already inputted
var str = formS.getRange("D6").getValue(); // The ID to find and to update in the "Inputs" sheet
var values = dataS.getDataRange().getValues();
for (var i = 0; i<=values.length; i++) {
var rowFinder = values[i];
if (rowFinder[columnIndex] == str) {
var rowNumber = i+1;
var updatedValues = [[formS.getRange("D9").getValue(),
formS.getRange("D13").getValue(),
formS.getRange("D14").getValue(),
formS.getRange("D15").getValue(),
formS.getRange("D16").getValue(),
formS.getRange("D17").getValue(),
formS.getRange("D18").getValue(),
formS.getRange("D19").getValue()]];
dataS.getRange(rowNumber, 2, 1, 17).setValues(updatedValues);
}
break;
}
}
Find also an example of the sheet that I'm using, where you could also see the whole code (everything else is working fine). Spreadhsheet here
Why function is not updating the data? I've tried to put the dataS.getRange(rowNumber, 2, 1, 17).setValues(updatedValues); in different parts of the code and it doesn't work either. Any ideas?
It is best to get the values of a range all at once and iterate through the entire range than pulling individual cells.
I tested this on your sample spreadsheet (Thanks for including!)
function SubmitData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("App"); // Add the name of the sheet where the data is going to be retrieved
var dataS = ss.getSheetByName("Results"); // Name of the sheet where the data should appear
//get the entire form range
var values = formS.getRange(6, 3, 14, 2).getValues();
//Create an array to paste into the Results tab
var pasteArr =[];
//iterate through the two columns on the App tab, only add values to the paste array
//if Col C is not blank and does not equal "Choose Params"
for (var i in values) {
//the values array looks like this [[row1Col1, row1Col2],[row2Col1, row2Col2]..]
//so to get to the value in row i, column 1 you use values[i][0]
let field = values[i][0];
if (field != "" && field != "Choose Params") {
let val = values[i][1];
pasteArr.push(val);
}
};
Logger.log(pasteArr);
var dataLastRow = dataS.getLastRow() + 1;
//Build the range to paste in - use the length of pasteArr to get the proper number of columns
//then to make pasteArr a 2D array put pasteArr inside another array by using square brackets
dataS.getRange(dataLastRow, 1, 1, pasteArr.length).setValues([pasteArr]);
Logger.log("Done");
ClearCell();
}
Your other functions have similar issues.
function Update() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("App"); // From sheet
var dataS = ss.getSheetByName("Results"); // Get the data already inputted
var dataVals = dataS.getDataRange().getValues();
//get the entire form range
var values = formS.getRange(6, 3, 14, 2).getValues();
//Create an array to paste into the Results tab
var pasteArr =[];
//iterate through the two columns on the App tab, only add values to the paste array
//if Col C is not blank and does not equal "Choose Params"
for (var i in values) {
let field = values[i][0];
if (field != "" && field != "Choose Params") {
let val = values[i][1];
pasteArr.push(val);
}
}
let numCols = pasteArr.length;
//iterate through the rows on the data tab, update the row with the matching ID
for (var j in dataVals) {
let dataTableId = dataVals[j][0];
let formID = pasteArr[0];
Logger.log(dataTableId + " , "+formID);
if (dataTableId === formID){
//have to convert j from 0 index to columns by adding 1. The + before the j and the 1 force app script to treat it as a number.
dataS.getRange(+j+ +1, 1, 1, numCols).setValues([pasteArr]);
Logger.log("dataTableId matches");
}
}
Logger.log("Done");
}
Related
I have a list of 290 items with 4 columns, which I need to duplicate. I have in a Spreadsheet Row some departements and under it a lot of systems. for each system I need to duplicated the 290 values and add one index column at the front, the department in column 6 and the system in column 7.
I am using the following code:
const ssOrg = SS.getSheetByName("OrgStructure");
function myFunction() {
var afinal = [];
var aDevs = ssDeliverables.getDataRange().getValues();
aDevs.shift();
var lastRow = ssOrg.getLastRow();
var lastColum = ssOrg.getLastColumn();
var count = 1
for (var spalte = 1; spalte <lastColum; spalte++){
var squad = ssOrg.getRange(3,spalte).getValue();
for (var reihe=5; reihe <lastRow; reihe++) {
var system = ssOrg.getRange(reihe,spalte).getValue();
if (system !== ""){
aDevs.map(function(row){
row[0] = count;
row[5] = squad;
row[6] = system;
count ++
return row
})
Logger.log(system);
afinal = afinal.concat(aDevs);
}
}
}
var lastDataRow = ssAssessmentLogic.getLastRow();
ssAssessmentLogic.getRange(2,1,lastDataRow-1,10).clearContent();
var rngResult = ssAssessmentLogic.getRange(2,1,afinal.length,7);
rngResult.setValues(afinal);
}
The problem is that the array at the end (16000 rows) has the same value for each row in column 6 and 7. It is allways the last system & department combination that appears in all 16000 rows.
Where am I wrong?
The question was a little confusing for me but I followed your specifics in the comments section where you explain what exactly info to copy and how and where to paste it.
This gets the job done:
const ss = SpreadsheetApp.getActive();
// this gets the "deliverables" and the "departments", this last ones in a list
// and for each department runs the function to add the complete new modified array to the spreadsheet
function fillsNewSheet(){
var newSheet = ss.getSheetByName('List_DeliverablesALL');
// hardcoded the titles
newSheet.getRange(1, 1, 1, 6).setValues([['taskHasDeliverableNumber', 'Deliverable Description', 'SDP Task', 'SDP Milestone', 'Group', 'Department']])
var deliverables = getDeliverables();
var departments = getDepartments();
for(i=0;i<departments.length;i++){
var departmentsGroup = departments[i];
for(j=0;j<departmentsGroup.length;j++){
addsNewSection(deliverables, departmentsGroup[j])
}
}
}
// this just gets de array of values we are gonna paste for each department in the structure sheet.
function getDeliverables(){
var deliSheet =ss.getSheetByName('Deliverables1');
var deliValues = deliSheet.getRange(2, 2, deliSheet.getLastRow()-1, 4).getValues();
return deliValues;
}
// As the departments are in different columns with different row counts,
// I go over the whole columns and rows and create a single list with all "department" and "group" pairs
function getDepartments(){
var structureSheet = ss.getSheetByName('OrgStructure');
var cols = structureSheet.getLastColumn();
var groups = structureSheet.getRange(3, 1, 1, cols).getValues()[0]
var departments = [];
for(i=1;i<=cols;i++){
var group = groups[i-1];
var groupDeps = structureSheet.getRange(5, i, structureSheet.getLastRow(), 1).getValues();
var subDeps = []
for(j=0;j<groupDeps.length;j++){
subDeps.push([group, groupDeps[j][0]])
}
var filtered = subDeps.filter( function (data) { return data[1] != "" });
departments.push(filtered);
}
return departments;
}
// finally this gets the complete list of "deliverables" from the first sheet, and one specific department.
function addsNewSection(deliverables, department){
var newSheet = ss.getSheetByName('List_DeliverablesALL');
// and in every row of the deliverables list we add the corresponding department/group pair to get the new modified array.
var newSection = []
for(k=0;k<deliverables.length;k++){
var newRow = deliverables[k].concat(department)
newSection.push(newRow)
}
// when this is complete I paste the whole new array in the third sheet.
newSheet.getRange(newSheet.getLastRow()+1, 1, newSection.length, 6).setValues(newSection)
}
UPDATE WITH a-change 's response and code
I am working on a function that will let me select a range in a sheet in Google Sheets and then paste the values that I am interested in into a specific order on another sheet.
Suppose RawData (Sheet1) looks like this:
I want to grab the range RawData!A1:L15, so basically everything that is that picture.
Afterwards I want to print it in another sheet (Sheet2 called Analysis) like so:
So far this is the code:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("RawData");
var targetSheet = ss.getSheetByName("Analysis");
var range = sheet.getDataRange();
var values = range.getValues();
for (var i in values) {
var row = values[i];
var column = 1;
for (var j in row) {
var value = row[j];
if (value !== "X") {
targetSheet.getRange(parseInt(i) + 1, column).setValue(value);
column++;
}
}
}
}
This code results in values being pasted in the 'Analysis' with the same order as in the 'RawData' sheet. The idea is for the data to be able to be pasted in a trio format, with no spaces between values. So the first trio would be: A1 = 1, B1 = 2, C1 = 3, A2 = 4, B2 = 5, C2 = 6, and so on.
A couple of things:
for (var row in values) { — here row is an index of an element, not the element itself. So it'll always be not equal to "X". Better to put it this way:
for (var i in values) {
var row = values[i];
}
Then you need to iterate over row to get to a single element and compare it with "X":
for (var i in values) {
var row = values[i];
for (var j in row) {
var value = row[j];
if (value !== "X") {
}
}
}
Next thing is pasting the value to your target sheet. The reason you are getting the same number in all the cells is that you're calling setValue on the whole A1:C8 cells range instead of one particular cell.
for (var i in values) {
var row = values[i];
var column = 1;
for (var j in row) {
var value = row[j];
if (value !== "X") {
targetSheet.getRange(parseInt(i + 1), column).setValue(value);
column++;
}
}
}
targetSheet.getRange(i, j) here gives you a single-cell precision.
So alltogether your code would look something like:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("RawData");
var targetSheet = ss.getSheetByName("Analysis");
var range = sheet.getDataRange();
var values = range.getValues();
for (var i in values) {
var row = values[i];
var column = 1;
for (var j in row) {
var value = row[j];
if (value !== "X") {
targetSheet.getRange(parseInt(i) + 1, column).setValue(value);
column++;
}
}
}
}
See how the target sheet is set as a variable instead of using a range on the source sheet — it gives you more readability and freedom
It seems that when iterating like for (var i in row) i is considered to be a string so the parseInt call
column variable is needed to make sure there are no empty cells in the target sheet
I've also changed sheet.getRange(1,1,15) to sheet.getDataRange() to make sure your code gets all the data in the sheet
The approach of setting values into single cells separately is not optimal. It should work for you in your case as the data range seems pretty small but as soon as you get to hundreds and thousand of rows, you'll need to switch to setValues, so you'll need to build a 2D-array before pasting the values. The tricky thing is that your resulting rows may have a variable number of items (depending on how many Xs are in a row) while setValues expects all the rows to be of the same length — it's possible to get round it of course.
I'm new to Google Apps Script and I'm trying to ignore the empty rows from a for loop, but I'm still getting the empty rows in my log. Here are my codes,
function getNonEmptyRows() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Bldng4");
var lr = sheet1.getLastRow() - 17;
for (var i = 1; i < lr; i++) {
var singleRow = sheet1.getRange(i, 1, 1, sheet1.getLastColumn()).getValues();
if (singleRow.length > 0) {
Logger.log(singleRow);
}
}
}
How can I get the only non empty rows from the loop? Need this help badly. Thanks.
var range_data =
sheet1.getRange("A2:A") //Column Range
.getValues() //Get array from range values
.filter(array=>array != '') //Filter non-empty values
I was looking for a solution to a similar problem and here is what I did:
first, I found this tutorial on how to create google apps script to eliminate duplicate rows.
next, i modified it like this to eliminate empty rows:
function removeEmptyRows() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("settings");
var data = sheet.getRange("A2:D").getValues();
var newData = new Array();
for(i in data){
var row = data[i];
var empty = false;
for(i in data){
if(row.toString() == ",,,"){
empty = true;
}
}
if(!empty){
newData.push(row);
}
sheet.getRange(2, 6, newData.length, newData[0].length).setValues(newData);
};
As you can see, it takes A2:D range, removes empty rows and then pastes filtered data without empty rows into range F2:I.
You can try to use this script, but you may need to adjust it to "width" of your array. To do so change the number of commas in the following string:
if(row.toString() == ",,,"){
edit:
I modified script a bit to automatically adjust to width of your array:
function removeEmptyRows() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("settings");
var range = sheet.getRange("A2:C");
var data = range.getValues();
var dataWidth = range.getWidth();
var newData = new Array();
if (dataWidth<=1) {
var stringToCompare = '';
}else{
var stringToCompare = ',';
for (var i=0;i<dataWidth-2;i++) stringToCompare+=","
};
for(i in data){
var row = data[i];
var empty = false;
for(i in data){
if(row.toString() == stringToCompare){
empty = true;
}
}
if(!empty){
newData.push(row);
}
}
sheet.getRange(2, 6, newData.length, newData[0].length).setValues(newData);
};
The getValues() method you are using always return a 2 dimensions array, whatever the contents of the cells might be. There are several ways to get the cells content, one of them is to stringify the range content (convert matrix to single string) , remove the commas (and eventually "invisible" spaces) and check the length of the resulting string.
replacement code could go like this :
var singleRow = sheet1.getRange(i, 1, 1, sheet1.getLastColumn()).getValues().toString().replace(',','').replace(' ','');
That said, this code is very inefficient because it uses SpreadSheetApp service in each loop iteration which is particularly slow.
You'll find better approches in the documentation about best practices.
I modified the code this way... it gathers data from all the sheets and finds only the rows that have data, BUT now I am having a problem modifying the range with each pass so that it is equal to the number of rows that do have value (found with (values[row][0] != '')). I have put a ??? in the spot where I am trying to have a variable height.
function getAllData() {
var folder = DocsList.getFolderById("folderid");
var contents = folder.getFiles();
Logger.log("file length: " + contents.length);
var file;
var data;
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Base")
sheet.clearContents();
var numOfFiles = contents.length;
for (var i = 0; i < numOfFiles; i++) {
file = contents[i];
Logger.log("count: " + i);
var theFileType = file.getFileType();
Logger.log("theFileType: " + theFileType);
if (theFileType==DocsList.FileType.SPREADSHEET) {
var sheet2 = SpreadsheetApp.open(file).getSheetByName("Sheet 1");
var lastLine = sheet2.getLastRow();
var values = sheet2.getRange('A3:J').getValues();
var formulas = sheet2.getRange('A3:J').getFormulas();
var data = [];
for(var row = 0 ; row < (values).length ; row++){
var lastrow = sheet.getLastRow()+1;
if (values[row][0] != '') {
for(var col = 0 ; col < formulas[row].length ; col++){
if(formulas[row][col] != '')
{values[row][col] = formulas[row][col]};
data.push(values[row]);}
if(data.length > 0)
sheet.getRange(lastrow, 1, ???, data[0].length).setValues(data);
}
}
};
}}
You are using getValue() as opposed to getValues() (With a letter "s" on the end)
var onecell = posheet.getRange('B4').getValue();
The documentation states:
getValue() - Returns the value of the top-left cell in the range.
The parameter for getRange() is kind of tricky and not well documented.
For example this:
getRange(2, 3, 6, 4)
gets a range from C2 to G8. Figure that out. The first number is the number 2, which is for the row 2. The second number is 3, for the third column (which is C). The third and fourth numbers are relative to the first two numbers.
Also, you are using: appendRow([array]) which uses an array for the parameter. So you must make sure that the data is in the form of an array, or use something else.
Here is the link for getValues:
Google Documentation - getValues
The example is this code:
// The code below will get the values for the range C2:G8
// in the active spreadsheet. Note that this will be a javascript array.
var values = SpreadsheetApp.getActiveSheet().getRange(2, 3, 6, 4).getValues();
Logger.log(values[0][0]);
Here is code that seems to work:
function getAllData() {
var folder = DocsList.getFolderById("Your file ID");
var contents = folder.getFiles();
Logger.log("file length: " + contents.length);
var file;
var data;
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1")
sheet.clearContents();
sheet.appendRow(["Value from Sheet One", "Range of values from Sheet Two"]);
var numOfFiles = contents.length;
for (var i = 0; i < numOfFiles; i++) {
file = contents[i];
Logger.log("count: " + i);
//Reset to null on every iteration
var onecell = null;
var theRange = null;
var theFileType = file.getFileType();
Logger.log("theFileType: " + theFileType);
if (theFileType==DocsList.FileType.SPREADSHEET) {
var sheet1 = SpreadsheetApp.open(file).getSheetByName("Sheet1");
var sheet2 = SpreadsheetApp.open(file).getSheetByName("Sheet2");
// The code below will get the values for the range A3:A9
// in the active spreadsheet. Note that this will be a javascript array.
onecell = sheet1.getRange('B4').getValue();
theRange = sheet2.getRange(1,3,1,6).getValues();
Logger.log('onecell: ' + onecell);
Logger.log('onecell[0][0]: ' + onecell[0][0]);
Logger.log('theRange: ' + theRange)
Logger.log('theRange[0][0]: ' + theRange[0][0])
var multipleValues = [theRange[0][0], theRange[0][1], theRange[0][2], theRange[0][3], theRange[0][4]];
Logger.log('multipleValues: ' + multipleValues);
sheet.appendRow([onecell, "'" + multipleValues]);
};
}
}
In the first column, it only enters one value into the sheet cell. In the second column, the cell gets multiple values put into it from the row. In other words, and entire rows values, and combined and put into one cell. I think that's what you want from the code.
If you try to put an array into a spreadsheet cell, instead of showing the array of values as text, it shows something like an object. So I put a quote in front of the values so the cell formatting would default to text.
I have 2 spreadsheet in my Drive. I want to pull data from a cell in 1 spreadsheet and copy it in another.
The spreadsheet "TestUsage" will sometimes have data in column A, but none is column B.
I would like so that when I open the spreadsheet, it would populate that empty cell in sheet "TestUsage" from sheet "TestParts".
Here is my code:
var ssTestUsage = SpreadsheetApp.getActiveSpreadsheet();
var sTestUsage = ssTestUsage.getActiveSheet();
var lastRowTestUsage = sTestUsage.getLastRow();
var rangeTestUsage = sTestUsage.getSheetValues(1, 1, lastRowTestUsage, 4);
var TESTPARTS_ID = "1NjaFo0Y_MR2uvwit1WuNeRfc7JCOyukaKZhuraWNmKo";
var ssTestParts = SpreadsheetApp.openById(TESTPARTS_ID);
var sTestParts = ssTestParts.getSheets()[0];
var lastRowTestParts = sTestParts.getLastRow();
var rangeTestParts = sTestParts.getSheetValues(1, 1, lastRowTestParts, 3);
function onOpen() {
for (i = 2; i < lastRowTestUsage; i++) {
if (rangeTestUsage[i][0] !== "" && rangeTestUsage[i][1] == "") {
for (j = 1; j < lastRowTestParts; j++) {
if (rangeTestUsage[i][0] == rangeTestParts[j][0]) {
Logger.log(rangeTestUsage[i][1]);
Logger.log(rangeTestParts[j][1]);
rangeTestUsage[i][1] = rangeTestParts[j][1];
break;
}
}
}
}
}
The problem with this is this doesn't do anything:
rangeTestUsage[i][1] = rangeTestParts[j][1];
I know there must be a method that can get data from one range to another.
Please let me know if I am totally incorrect or I am on the right path.
the statement
"this doesn't do anything:"
rangeTestUsage[i][1] = rangeTestParts[j][2];
is not really true... and not really false neither..., actually it does assign the value to rangeTestUsagei but you dont see it because it is not reflected in the spreadsheet.
Both values are taken from the Sheet using getValues so at that time they are both array elements.
What is missing is just writing back the array to the sheet using the symetrical statement setValues()
Give it a try and don't hesitate to come back if something goes wrong.
EDIT :
I didn't notice at first that you were using getSheetValues instead of getValues (simply because I never use this one)... the only difference is that getValues is a method of the range class while yours belongs to the sheet class; the syntax is similar in a way, just use
Sheet.getRange(row,col,width,height).getValues()
it takes one word more but has the advantage to have a direct equivalent to set values
Sheet.getRange(row,col,width,height).setValues()
Serge insas has a good explanation of why your code doesn't work and hints at the solution below.
I recommend you use an array to store the updated values of column B that you want then set the entire column at the end.
Modifying your code...
var ssTestUsage = SpreadsheetApp.getActiveSpreadsheet();
var sTestUsage = ssTestUsage.getActiveSheet();
var lastRowTestUsage = sTestUsage.getLastRow();
var rangeTestUsage = sTestUsage.getSheetValues(1, 1, lastRowTestUsage, 2);
var TESTPARTS_ID = "1NjaFo0Y_MR2uvwit1WuNeRfc7JCOyukaKZhuraWNmKo";
var ssTestParts = SpreadsheetApp.openById(TESTPARTS_ID);
var sTestParts = ssTestParts.getSheets()[0];
var lastRowTestParts = sTestParts.getLastRow();
var rangeTestParts = sTestParts.getSheetValues(1, 1, lastRowTestParts, 2);
var colB = [];
function onOpen() {
for (i = 2; i < lastRowTestUsage; i++) {
if (rangeTestUsage[i][0] !== "" && rangeTestUsage[i][1] == "") {
var matched = false;
for (j = 1; j < lastRowTestParts; j++) {
if (rangeTestUsage[i][0] == rangeTestParts[j][0]) {
//Logger.log(rangeTestUsage[i][1]);
//Logger.log(rangeTestParts[j][1]);
colB.push([rangeTestParts[j][1]]); // push the value we want into colB array
matched = true;
break;
}
}
if(!matched) // this is in case you don't have a match
colB.push([""]); // incase we don't have a matching part
} else {
colB.push([rangeTestUsage[i][0]]); // we already have a value we want so just add that to colB array
}
}
sTestUsage.getRange(2,2,lastRowTestUsage).setValues(colB); // update entire column b with values in colB array
}