I'm fairly new to coding in Google Script, and with Javascript. Basically what I'm trying to do is make a script to update data on a table in a spreadsheet. I have the script to import the email as a CSV, but I'm struggling with transferring the data from the email to the table by matching up the dates. Essentially what I would like the script to do is emulate a vlookup and paste the values from the emails CSV file to the table.
I made a copy of the file as an example of what I'm trying to do. I'm trying to transfer the yellow section of columns A and B of the Data tab to the matching yellow section columns A and B. And if there is no data for the dates then I would like the empty dates to be 0.
https://docs.google.com/spreadsheets/d/1uK3sCUFvcW6lgk962jgTN-yZox-lF8-Z0wm7Zhh-i8I/edit?usp=sharing
Thanks!
This two functions will accomplish your objectives. createArray(hight, width, filling) is just a workaround to create an array of the exact size of the Destination table. moveDates() is the one that compares the timestamps of the Data table with the ones on Destination; and will write down the values of the row if they match, and a zero if they don't.
This second function will first declare a bunch of variables that will save ranges and values for both sheets. After that, it will read all the dates of both tables. Later, it will run through the Destination table searching for coincidences and saving them on the newData array. Finally, the code will write down the newData. I've tested this code on your spreadsheet and it works perfectly.
function createArray(hight, width, filling) {
var array = [];
for (var i = 0; i < hight; i++) {
array[i] = [];
for (var j = 0; j < width; j++) {
array[i][j] = filling;
}
}
return array;
}
function moveDates() {
var dataSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Data');
var destinationSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(
'Destination');
var dataRange = dataSheet.getRange(5, 1, 6, 3);
var destinationRange = destinationSheet.getRange(2, 1, 11, 3);
var newDataRange = destinationSheet.getRange(2, 2, 11, 2)
var data = dataRange.getValues();
var destination = destinationRange.getValues();
var dataDates = [];
var destinationDates = [];
var newData = createArray(11, 2, 0);
for (var i = 0; i < data.length; i++) {
dataDates.push(new Date(data[i][0]));
}
for (var i = 0; i < destination.length; i++) {
destinationDates.push(new Date(destination[i][0]));
}
for (var i = 0; i < destination.length; i++) {
for (var j = 0; j < data.length; j++) {
if (destinationDates[i].getTime() === dataDates[j].getTime()) {
newData[i][0] = data[j][1];
newData[i][1] = data[j][2];
}
}
}
newDataRange.setValues(newData);
}
If you need more information or clarifications I'll be happy to help you.
Related
I have a table that can be dynamically styled with various colors and a palette of colors that serves as a reference to those colors. When the user has finished coloring, I want to compile the file to JSON. I can fetch the necessary values likewise:
// note _colors is a global variable declared elsewhere
var $rowSelect = $("#rowSelect");
var $colSelect = $("#colSelect");
var $colorBoard = $("#colorBoard");
var $upload = $("#upload");
$upload.on("click", function() {
var numRows = Number($rowSelect.val());
var numCols = Number($colSelect.val());
var pixels = [];
var colors = [];
var comp_colors = [];
var $rows = $colorBoard.children();
for(var i = 0; i < numRows; i++) {
var $cols = $rows.eq(i).children();
for(var ii = 0; ii < numCols; ii++) {
$cell = $cols.eq(ii);
pixels.push(Number($cell.attr("data-colorid")));
}
}
var usedColors = pixels.filter((v, i, a) => a.indexOf(v) === i);
for(var i = 0; i < _colors.length; i++) {
colors.push(_colors[i] ? _colors[i].color.getColor() : null);
if(_colors[i] && usedColors.includes(i) && !comp_colors.includes(colors[i])) comp_colors.push(_colors[i].color.getColor());
}
}
Since colors can be deleted, duplicated, or unused in my UI, I cleaned up my raw color array colors into the comp_colors array. For example, an array of unprocessed colors may look like: [null, "#EF1A1A", null, "#40E255", "#0B1DE3", "#FFFFFF", "#FFFFFF"]. If "#0B1DE3" was not actually present in our pixel array, the cleaned up version would look like: ["#EF1A1A", "#40E255", "#FFFFFF"].
The problem is that cleaning up the color array has offset the indexes that the values of the pixels array were using to reference the colors. For example, using the color situation above, a fetched array of pixels may look like this: [1,3,3,1,5,6,5,1,3,6], but the revised version for the cleaned up color set would be: [0,1,1,0,2,2,2,0,1,0].
What is the most efficient way to correct these values to the appropriate indexes?
I found a concise method, but I don't feel like it is very efficient. I have to iterate through every pixel to achieve this, which can be costly at with large creations. The new list of pixels will be loaded into comp_pixels:
for(var i = 0; i < cells.length; i++) comp_pixels.push(comp_colors.indexOf(colors[pixels[i]]));
Hi guys im using google app script trying to get the data from google form to transpose from raw data to sorted table. But my code is not working. Im trying to do a custom function. and call for =columnSplit(A1:B2, 2, ",").
This is what i have:
the dates all on column A and Concatenate numbers at number B.
9/10/17 13:30:00 1234,4567,8910
9/11/17 12:34:00 0987,6543,21
what i want to get:
9/10/17 13:30:00 1234
9/10/17 13:30:00 4567
9/10/17 13:30:00 8910
9/11/17 12:34:00 0987
9/11/17 12:34:00 6543
I took my reference from here: How to split and transpose results over 2 columns
function columnSplit(reference, index, delimiter) {
var input = reference;
var output = [];
if (input.constructor !== Array) {
input = [[input]];
}
for (var i = 0; i < input.length; i++) {
var parts = input[i][index - 1].toString().split(delimiter);
for (var j = 0; j < parts.length; j++) {
var copy = input[i].slice(0);
copy[index - 1] = parts[j].trim();
output.push(copy);
}
}
return output
}
This works:
function R2C(a1,idx,dlm){
var vA=a1;
var oA=[];
for(var i=0;i<vA.length;i++){
var tA=vA[i][idx].toString().split(dlm);
var oidx=(idx==1)?0:1;
for(k=0;k<tA.length;k++){
if(idx==0){
oA.push([tA[k],vA[i][1]]);
}else{
oA.push([vA[i][0],tA[k]]);
}
}
}
return oA;
}
These are my spreadsheets which show how to use it:
For this function a1 is a selected range. idx is either 0 or 1. dlm is the delimiter.
I have a sheet like this:
And I have this function:
function getRangeAsArrays(sheet) {
var dataRange = sheet.getRange(2, 1, sheet.getLastRow(), sheet.getLastColumn());
var data = dataRange.getValues();
var array = [];
for (var r=0; r<sheet.getLastColumn(); r++) {
for (i in data) {
var row = data[i];
array.push(row);
}
}
return array;
}
Which I use to build a listboxthis way:
var recipientArray = getRangeAsArrays(activeSheet);
var item3Panel = app.createHorizontalPanel();
item3Panel.add(app.createLabel("recipient"));
var listBox = app.createListBox().setName('item3');
for(var i = 0; i < (recipientArray.length); i++){
Logger.log("recipientArray[i][2] = " + recipientArray[i][3]);
Logger.log(" i = " + i);
listBox.addItem(recipientArray[i][4]);
}
item3Panel.add(listBox);
But when I iterate over the array length (4 rows), I got this (unexpected to me) result and the logs shows i variable goes until 14:
Since recipientArray.length should give me the first dimension of the 2 dimensional array and recipientArray[i].length the second dimension, and since I want the first dimension (number of rows) row to fix that? What is going wrong here?
Even if I'm still unsure that I understood what you need (I guess I'm a bit tired or I become stupid... go figure...:), I wonder why you try using separate functions since the value returned by range.getValues() is already an array... A 2D array but still an array.
If you want to create one listBox per row and add the following cells as items then a double loop like this will do the job. (tell me if I'm completely off the subject, thx).
I wrote an example code with the main structure and comments to explain where things go.
function test() {
var dataRange = SpreadsheetApp.getActiveSheet().getRange(2, 1, sheet.getLastRow(), sheet.getLastColumn());
var data = dataRange.getValues();
Logger.log(data)
var array = []; // this is useless
for (var r=0; r<data.length; r++) {
// create listBox widget here
//var listBox = app.createListBox().setName('listBox'+r);
for (i in data[0]) {
var cell = data[r][i];
//add items to listBox here
// listBox.addItem(cell);
array.push(cell);// this is useless
}
}
Logger.log(array);//useless
return array;//useless
}
I'm attempting to grab the values from a data range, loop over the data, match a value in that data, then based on a matching value, update cell located a few columns over.
I'm able to locate value to match, but I'm having a hard time understanding how to update the cell a few columns over. Below is the code I've gotten so far minus the .setValue piece.
var trackingSS = 'Spreadsheet 1';
var decisionSS = 'Spreadsheet 2';
function grabRequestID() {
var ss = SpreadsheetApp.openById(decissionSS);
var range = ss.getActiveSheet().getRange(ss.getLastRow(), 2, 1, 1)
var requestID = range.getValue();
return requestID;
}
function managersDecision() {
var ss = SpreadsheetApp.openById(trackingSS)
var sheet = ss.getSheetByName('Requests');
var data = sheet.getDataRange().getValues();
var requestID = grabRequestID();
for (var i=0; i < data.length; i++) {
for (var j=0; j < data[i].length; j++) {
if (data[i][j] == requestID) {
Logger.log('found it');
}
}
}
}
As you can see there are two functions. managersDecision() reads in all the data from spreadsheet 1. It then calls grabRequestID() and uses this value (from spreadsheet 2) as the matching criteria as it loops over the data from spreadsheet 1. Currently it will locate and find the match.
What I want to have happen now, is based on the match, go over two columns in the same row and update the cell value to "approved" or "denied" based on successfully finding a match.
I'm a bit lost on how to get it to write to the cell. Should I try and identify the row its in and then work to set the value? Maybe grab the entire row the match is in (into an array) and then rewrite the row?
Any assistance would be appreciated..
To set a value you just need to take in count that you are working with an array that start at zero, but in the spreadsheet we start counting at one. You'll also need to be sure that you are trying to write in an existing cell. So prior writing, add the necessary column.
I didn't wrote the "denied" part as it was going through all the cell of the spreadsheet, but I wrote a second version of the managersDecision function where I only go through one column and here I took care of that denied part.
here the code:
var trackingSS = 'Spreadsheet1';
var decisionSS = 'Spreadsheet2';
function grabRequestID() {
var ss = SpreadsheetApp.openById(decisionSS);
var range = ss.getActiveSheet().getRange(ss.getLastRow(), 2, 1, 1)
var requestID = range.getValue();
Logger.log("requestID= "+requestID);
return requestID;
}
function managersDecision() {
var ss = SpreadsheetApp.openById(trackingSS)
var sheet = ss.getSheetByName('Requests');
var data = sheet.getDataRange().getValues();
var requestID = grabRequestID();
for (var i=0; i < data.length; i++) { // going through all the rows
for (var j=0; j < data[i].length; j++) { // this is going through all the cell of a row
if (data[i][j] == requestID) {
Logger.log('found it');
var row = Number(i)+1;
var col = Number(j)+1+2;
while(sheet.getMaxColumns()<col){
sheet.insertColumnsAfter(sheet.getMaxColumns(),col-sheet.getMaxColumns());
}
sheet.getRange(row, col).setValue("approved");
}
}
}
}
function managersDecision2() {
var ss = SpreadsheetApp.openById(trackingSS)
var sheet = ss.getSheetByName('Requests');
var data = sheet.getRange("A:A").getValues()
var requestID = grabRequestID();
var col = 1+2;
while(sheet.getMaxColumns()<col){
sheet.insertColumnsAfter(sheet.getMaxColumns(),col-sheet.getMaxColumns());
}
for (var i=0; i < data.length; i++) { // going through all the rows
var row = 1+i;
if (data[i][0] == requestID) {
Logger.log('found it');
sheet.getRange(row, col).setValue("approved");
}
else if(data[i][0] !=""){
Logger.log(row)
sheet.getRange(row, col).setValue("denied");
}
}
}
I have been working on a scheduling website for the past few weeks. I am showing the schedules as PHP generated html-tables. I use merged cells for showing events. I have come to a problem when trying to delete events using JS. Since those are merged cells, using rowspan, I have to go through the table and re-adding empty cells whenever there is a need when I delete one. My solution is working fine for when my table contains one merged cell among nothing but empty cells, but with a more complex table, it fails. I can't really grasp what's wrong with it, except that it doesn't correctly find the cellIndex anymore. Does anyone have a clue? Here is what I'm talking about:
http://aturpin.mangerinc.com/table.html
(Click on an event to remove it, or attempt to anyhow)
This sample may help you find your solution. It seems to demonstrate your problem as well as have some sample code to generate a matrix to help you solve it.
EDIT: I liked the puzzle and decided to play with it for a bit, here is a "functioning" example of implementing that sample (although sometimes the table doesn't seem to redraw correctly. This should probably help you get further along the way.
function getTableState(t) {
var matrix = [];
var lookup = {};
var trs = t.getElementsByTagName('TR');
var c;
for (var i=0; trs[i]; i++) {
lookup[i] = [];
for (var j=0; c = trs[i].cells[j]; j++) {
var rowIndex = c.parentNode.rowIndex;
var rowSpan = c.rowSpan || 1;
var colSpan = c.colSpan || 1;
var firstAvailCol;
// initalized the matrix in this row if needed.
if(typeof(matrix[rowIndex])=="undefined") { matrix[rowIndex] = []; }
// Find first available column in the first row
for (var k=0; k<matrix[rowIndex].length+1; k++) {
if (typeof(matrix[rowIndex][k])=="undefined") {
firstAvailCol = k;
break;
}
}
lookup[rowIndex][c.cellIndex] = firstAvailCol;
for (var k=rowIndex; k<rowIndex+rowSpan; k++) {
if(typeof(matrix[k])=="undefined") { matrix[k] = []; }
var matrixrow = matrix[k];
for (var l=firstAvailCol; l<firstAvailCol+colSpan; l++) {
matrixrow[l] = {cell: c, rowIndex: rowIndex};
}
}
}
}
// lets build a little object that has some useful funcitons for this table state.
return {
cellMatrix: matrix,
lookupTable: lookup,
// returns the "Real" column number from a passed in cell
getRealColFromElement: function (cell)
{
var row = cell.parentNode.rowIndex;
var col = cell.cellIndex;
return this.lookupTable[row][col];
},
// returns the "point" to insert at for a square in the perceived row/column
getPointForRowAndColumn: function (row,col)
{
var matrixRow = this.cellMatrix[row];
var ret = 0;
// lets look at the matrix again - this time any row that shouldn't be in this row doesn't count.
for (var i=0; i<col; i++)
{
if (matrixRow[i].rowIndex == row) ret++;
}
return ret;
}
};
}
function scheduleClick(e)
{
if (e.target.className != 'event')
return;
//Get useful info before deletion
var numRows = e.target.rowSpan;
var cellIndex = e.target.cellIndex;
var rowIndex = e.target.parentNode.rowIndex;
var table = e.target.parentNode.parentNode;
var tableState = getTableState(table);
var colIndex = tableState.getRealColFromElement(e.target);
//Deletion
e.target.parentNode.deleteCell(cellIndex);
//Insert empty cells in each row
for(var i = 0; i < numRows; i++)
{
var row = table.rows[rowIndex + i];
row.insertCell(tableState.getPointForRowAndColumn(rowIndex+i, colIndex));
}
}