I have the following situation:
Column G Column H
|Black, Brown, Grey| | dog |
|Calico | | cat |
|Green, Blue | | bird|
| ... | | ... |
I would like to split and fill down to this:
Column G Column H
|Black | |dog |
|Brown | |dog |
|Grey | |dog |
|Calico| |cat |
|Green | |bird|
|Blue | |bird|
|... | | ...|
Looking at the script to split a comma delineated cell (Function to split text in cell and create column) I can follow the split but it's the filling down portion that I am having trouble with. I understand I should set up a while loop with the first cell as var = i and the second cell as var = j. Dump the split contents of i to an array and then fill down the array.length. However, I can't seem to get the syntax correct.
I'm pretty new to JS and GAS so any help would be appreciated.
Thanks for the help.
-JH
Here's a custom function that should work for you:
/**
* Splits the array by commas in the column with given index, by given delimiter
* #param {A2:B20} range Range reference
* #param {2} colToSplit Column index
* #param {","} delimiter Character by which to split
* #customfunction
*/
function advancedSplit(range, colToSplit, delimiter) {
var resArr = [], row;
range.forEach(function (r) {
r[colToSplit-1].replace(/(?:\r\n|\r|\n)(\d|\w)/g,", ").split(delimiter)
.forEach(function (s) {
row = [];
r.forEach(function (c, k) {
row.push( (k === colToSplit-1) ? s.trim() : c);
})
resArr.push(row);
})
})
return resArr.filter(function (r) {
return r.toString()
.replace(/,/g, "")
})
}
This function can be used like this:
Related
I need to storetext all lines from a table where CODICE CATASTALE have a value
I add an image to show, I need to save all line in variable with storetext with this characteristic CODICE CATASTALE have a value, in the image I add 1 - 2 - 3 - 4 to explain line to store.
This is a relative storetext when CODICE CATASTALE have a value stored the line.
Here the page
nonsolocap.it/cap?k=56040
Image
After the execution such script in Selenium IDE table variable will contain data from the table. xpath_to_all_rows_with_CODICE_CATASTALE should be replaced with corresponding xpath.
store xpath count | xpath = xpath_to_all_rows_with_CODICE_CATASTALE | n
store | 0 | j
while | ${j} < ${n} |
store | | rowElement
store | 0 | i
while | ${i} < 7 |
store text | xpath = xpath_to_all_rows_with_CODICE_CATASTALE[${j}]/td[${i}] | element
execute script | if (${i} != 0) var arr = ${rowElement}; else var arr = []; var element = ${element}; arr.push(element); return arr; | rowElement
execute script | return Number(${i}) + 1; | i
end| |
execute script | if (${j} != 0) var arr = ${table}; else var arr = []; var rowElement = ${rowElement}; arr.push(rowElement); return arr; | table
execute script | return Number(${j}) + 1; | j
end| |
use scvSave command and give a name for the target for divide raws
I've been working on a time in/time out google sheet with a script. My original question was how to share a sheet to a user(let's call them client) where the Client can edit the sheet that had an onEdit trigger that would then set values into another sheet that the Client did not have permission to use.
I first tried just sharing a spreadsheet and making one of the sheets in that spreadsheet locked. But this prevented the trigger from running correctly when used by the client. So I asked about it and someone suggested using an installable edit trigger on the client sheet. And have the information be appended to a new spreadsheet. However, I don't think this is what I wanted because the way the code works needs the clients to not be able to edit the information that will be appended, not because I don't want false information but because if they get rid of certain columns, the script doesn't know what to do.
But it did give me an idea, instead of having one spreadsheet with two sheets, just have 2 spreadsheets with 1 sheet each. Much less likely for the people using the client account to find the shared spreadsheet vs finding the sheet in the open spreadsheet they are using.
So now to show you some code. Sorry I feel I have to show all of it due to any small thing that could be wrong I am over looking.
Host Spreadsheet Id is the Id of the spreadsheet the information is being logged into. This is the information I don't want being edited by the client.
ACTIVE refers to the name of the sheet the client is clicking on and editing. PASSIVE refers to the name of the sheet that is collecting information. Originally these two sheets were on the same spreadsheet.
function setValue(cellName, value) {
SpreadsheetApp.getActiveSpreadsheet().getRange(cellName).setValue(value);
}
function getCurrentRow() {
var currentRow = SpreadsheetApp.getActiveSheet().getActiveSelection().getRowIndex();
return currentRow;
}
function getValue(cellName) {
return SpreadsheetApp.getActiveSpreadsheet().getRange(cellName).getValue()
}
function getNextRow() {
var sas = SpreadsheetApp.openById("Host Spreadsheet Id");
var ss = SpreadsheetApp.setActiveSpreadsheet(sas)
return SpreadsheetApp.getActiveSpreadsheet().getSheetByName('PASSIVE').getLastRow() +1;
}
function getLasttRow() {
var sas = SpreadsheetApp.openById("Host Spreadsheet Id");
var ss = SpreadsheetApp.setActiveSpreadsheet(sas)
return SpreadsheetApp.getActiveSpreadsheet().getSheetByName('PASSIVE').getLastRow();
}
function addRecord(a, b, c, d) {
var sas = SpreadsheetApp.openById("Host Spreadsheet Id");
SpreadsheetApp.setActiveSpreadsheet(sas)
var row = getNextRow();
setValue('PASSIVE!A' + row, a);
setValue('PASSIVE!B' + row, b);
setValue('PASSIVE!C' + row, c);
setValue('PASSIVE!D' + row, d);
}
/// this function is to find the row number(s) that match the criteria in
/// the timeIn() function so that the time in date can be placed
/// directly to the right of the individual that signed out.
function findRows(c1,n1,c2,n2,c3,n3,name) {
var sas = SpreadsheetApp.openById("Host Spreadsheet Id")
SpreadsheetApp.setActiveSpreadsheet(sas)
var ss=SpreadsheetApp.getActiveSpreadsheet()
var sh=ss.getSheetByName(name);
var rg=sh.getDataRange();
var vA=rg.getValues();
var rA=[];
for(var i=0;i<vA.length;i++) {
if(vA[i][c1-1]==n1 && vA[i][c2-1]==n2 && vA[i][c3-1]==n3) {
rA.push(i+1);
}
}
return rA
}
function timeIn() {
var row = getCurrentRow()
var LocationA = getValue('ACTIVE!A' + row)
var LocationB = getValue('ACTIVE!B' + row)
var passiveRow = findRows(1,LocationA,2,LocationB,5,"",'PASSIVE');
Logger.log(passiveRow);
setValue('PASSIVE!E' + passiveRow, new Date().toLocaleString());
}
function places() {
var row = getCurrentRow()
addRecord(getValue('A' + row), getValue('B' + row), getValue('C' + row), new Date().toLocaleString());
}
function onEdit(e) {
var row = getCurrentRow()
var Location = getValue('ACTIVE!C' + row)
var LocationA = getValue('ACTIVE!A' + row)
var LocationB = getValue('ACTIVE!B' + row)
var passiveRow = findRows(1,LocationA,2,LocationB,5,"",'PASSIVE');
Logger.log(row)
Logger.log(Location)
Logger.log(LocationA)
Logger.log(LocationB)
Logger.log(passiveRow)
if(SpreadsheetApp.getActiveSheet().getName() !== "ACTIVE") return;
if(Location !== 'HOME' && Location !== "" && passiveRow == "") {
places();
Logger.log(passiveRow)
}
else if(Location !== 'HOME' && Location !== "" && passiveRow !== "") {
timeIn();
places();
Logger.log(passiveRow)
}
else if(Location === 'HOME' && passiveRow !== "") {
timeIn();
Logger.log(passiveRow)
}
}
So this was my attempt at turning what used to be 2 sheets in the same spreadsheet into 2 different spreadsheets. However, the onEdit(e) function doesn't work. If I manually run the timeIn() function, it works and the places() function works as well. Even goes and finds the last row of the spreadsheet it is importing information into. But alas, when they edit Column C, the column that is suppose to activate the onEdit(e) if else to work, it doesn't run the functions. What am I missing?
Let me give some visuals of what I'm trying to do.
Here is the sheet the clients should see. Column C has drop down boxes in each row full of locations.
First | Last | LOCATIONS
=================================
James | Carter | HOME
Kyle | Johnson | MALL
Micheal | Wilson | BANK
Sarah | Smith | HOME
Tray | Tin | CLINIC
John | Becks | HOME
Here would be the sheet collected by the Host Spreadsheet
First | Last | LOCATION | OUT | IN
=====================================================
Tray | Tin | CLINIC | 10:00 |
James | Carter | MALL | 12:30 | 1:30
Kyle | Johnson | MALL | 12:45 |
Micheal | Wilson | BANK | 01:00 |
James | Carter | POOL | 01:30 | 2:00
I hope this is a good enough visual to get the point across as to what I'm trying to accomplish. The code did work on the same spreadsheet, but now I'm trying to make it work on two in order to prevent the Client from messing with the Collected information. Because if something like this happens :
First | Last | LOCATION | OUT | IN
=====================================================
Tray | Tin | CLINIC | 10:00 |
James | Carter | MALL | 12:30 | 1:30
Kyle | Johnson | MALL | 12:45 |
Micheal | Wilson | BANK | 01:00 |
James | Carter | POOL | 01:30 | 2:00
Sarah | Smith | POOL | 01:30 |
Sarah | Smith | POOL | 01:31 |
You see Sarah Smith is on twice with no Sign in. This causes a bug because of how I get passive rows, it now sees 2 rows where "IN" is empty, so the function findRows() it gives me 2 values where I can only have 1 value in the function setValue() inside the timeIn() function.
I have just started trying out the script editor of the Google Sheets.
Here is what I am trying to do:
I have a few "headers" in Sheet A. I will name them Header A, B, and C.
1 | Header A
2 | text 1
3 | text 2
4 | (empty row)
5 | Header B
6 | text 1
7 | text 2
8 | (empty row)
9 | Header C
10 | text 1
11 | text 2
Sheet B
1 | text 1 | date | Header A
2 | text 2 | date | Header B
3 | text 3 | date | Header B
4 | text 4 | date | Header C
5 | text 5 | date | Header A
When I update my data on Sheet B, it will be automatically be updated on Sheet A based on my custom attributes in Sheet B. I need it to be able to update the data on Sheet B onto Sheet A's empty row after the specific Headers.
Currently I am stuck at getting the next empty row in Sheet A. I am able to get the row that my Headers are in but I couldn't find any help online finding the next empty row after each specific Headers.
I could not find any classes that is provided. All I can see is getLastRow which is not what I want. I wonder can this be done?
Below is my current code:
function getScheduleStatus(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var name = ss.getSheetByName("Production Schedule");
var rng = name.getDataRange();
var last_row = rng.getLastRow();
var data = rng.getValues();
var str_upcoming = "upcoming"
var rows = [];
for (var i=0; i < data.length; i++) {
if (data[i][0].toLowerCase() == str_upcoming) {
Logger.log("true");
rows.push(i);
}
}
var row = Number(rows)+Number(rng.getRow());
Logger.log(row);
}
Currently I am able to find the Header as well as getting the row number.
Hope you guys can understand.
Take a look at Faster way to find the first empty row; the answers there can be adapted to your situation.
The fastest solution there was Don Kirby's, and since you have a single column of data, it is ideal. We just need to change it to start searching from a particular headerRow!
/**
* Search column A starting at the given row, to find
* the first following empty row. Uses spreadsheet row numbers.
* From: https://stackoverflow.com/a/38194740/1677912
*
* #param {Number} headerRow Row number to start search at.
*
* #returns {Number} Target empty row.
*/
function getFirstEmptyRowAfter(headerRow) {
var spr = SpreadsheetApp.getActiveSpreadsheet();
var column = spr.getRange('A:A');
var values = column.getValues(); // get all data in one call
var ct = headerRow; // Start at row after headerRow (0-adjusted)
while ( values[ct][0] != "" ) {
ct++;
}
return (ct + 1); // (1-adjusted)
}
I have an issue and i'm looping on it! :| I hope someone can help me..
So i have an input file (.xls), that is simple but there are a row (lets say its "ROW1") that is like this:
ROW1 | ROW2 | ROW3 | ROW_N
765 | 1 | AAAA-MM-DD | ...
null | 1 | AAAA-MM-DD | ...
null | 1 | AAAA-MM-DD | ...
944 | 2 | AAAA-MM-DD | ...
null | 2 | AAAA-MM-DD | ...
088 | 7 | AAAA-MM-DD | ...
555 | 2 | AAAA-MM-DD | ...
null | 2 | AAAA-MM-DD | ...
There are no stardard here, like you can see.. There are some lines null (ROW1) and in ROW2, there are equal numbers, with different association to ROW1 (like in line 5 and 6, then in line 8 and 9).
My objective is to copy and paste the values from ROW1, in the ROW1 after when is null, till isn't null. Basically is to copy form previous step, when is null...
I'm trying to use the "Formula" step, by using something like:
=IF(AND(ISBLANK([ROW1]);NOT(ISBLANK([ROW2]));ROW_n=ROW1;IF(AND(NOT(ISBLANK([ROW1]));NOT(ISBLANK([ROW2]));ROW_n=ROW1;ROW_n=""));
But nothing yet..
I've tried "Analytic Query" but nothing too..
I'm using just stream a xls file input..
Tks very much, any help is very much appreciiated!!
Best Regardsd!
Well i discover a solution, adding a "User Defined Java Class" with the code below:
import java.util.HashMap;
private FieldHelper output_field, card_field;
private RowSet out, log;
private String previou_card =null;
public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException
{
if (first)
{
first = false;
out = findTargetRowSet("out");
output_field = get(Fields.Out, "previous_card");
} else {
Object[] r = getRow();
if (r == null) {
setOutputDone();
return false;
}
r = createOutputRow(r, data.outputRowMeta.size());
if (previous_card != null) {
output_field.setValue(r, previous_card);
}
if (card_field == null) {
card_field = get(Fields.In, "Grupo de Cartões");
}
String card = card_field.getString(r);
if (card != null && !card.isEmpty()) {
previous_card = card;
}
// Send the row on to the next step.
putRowTo(data.outputRowMeta, r, out);
}
return true;
After this i have to put a few steps but this help very much.
Thank you mates!!
Finally i got result. Please follow below steps
Below image is full transformation screen.
Data Grid Data will be like these. Sorry for that in my local i don't have Microsoft because of that i took Data Grid. Instead of Data Grid you can drag and drop Microsoft Excel Input step.
Drag and Drop one java script step and write below code.
Last step of transformation, drag and drop Select values step and select the columns.( These step is no necessary)
Final result will be like these.
Hope this helps.
I have a sheet, say it's called raw sheet like this:
Column Index | A | B |
David | 1 | 10 |
Jerry | 5 | 15 |
David | 1 | 50 |
Jerry | 6 | 20 |
David | 8 | 20 |
There are only limited values in Column Index. Like in this case only "David" and "Jerry".
I want to create another sheet, say it's called summary sheet that can summarize some value by the Column Index value, like this:
Column Index Summary | f(A,B) |
David | some value |
Jerry | some value |
The f(A,B) can be any kind of function that take use of all the values in the first sheet. One example: to add every row's A*B to get a new number. In this case, it would be:
Column Index Summary | f(A,B) |
David | 220 | that is 1*10 + 1*50 + 8*20
Jerry | 195 | that is 5*15 + 6*20
What should I do?
Here's a Google Sheets custom function for you. It will operate on any arbitrary table of numerical data, aggregating the numbers by any arbitrary (but simple) algebraic expression. (Expression must be valid in Javascript, e.g. "A * B", "A + B / C", or even "Math.pow(A,B)".) There's no error checking, so it's not fool-proof.
Examples:
=summary('raw sheet'!A1:C6,"A*B") Yes, you can refer to different sheets.
=summary(A1:C6,"A*A + B")
=summary(A1:C6,"Math.pow(A,B)")
Custom Function
/**
* Performs given formula on each row in table, aggregating (summing)
* row results by the key value in first column.
*
* See: http://stackoverflow.com/questions/26925283/how-do-i-get-add-and-sum-by-column-index-value-in-google-spreasheet/26942156#26942156
*
* #param {range} table Input data table, including headers
* #param {string} formula Mathematical function to peform on each
* row in table, using header values as
* function parameters.
* #param {int} sortType (Optional, default 1) 0: do not sort, 1: sort ascending, -1: sort descending
* #param {int} sortColumn (Optional, default 1) Column to sort by.
*
* #return {range} Summary table of results
* #customfunction
*/
function summary(table,formula,sortType,sortColumn) {
sortType = (sortType == undefined) ? 1 : sortType;
sortColumn = (sortColumn == undefined) ? 1 : sortColumn;
// Sort comparison function for ordering summary table
// uses sortType & sortColumn
function colCompare(a,b)
{
var col = sortColumn - 1;
var order = sortType;
if (!order) return 1;
else
return ((a[col] < b[col]) ? -order : ((a[col] > b[col]) ? order : 0));
}
var headers = table[0];
// Start results with its header row
var summaryTable = [[headers[0],String(formula)]];
// evaluate formula, replacing variables (headers) with references to table
for (var h = 1; h < headers.length; h++) {
var re = new RegExp(headers[h],"g");
formula = formula.replace( re, " table[row]["+parseInt(h)+"] " );
}
// Aggregate data by summing formula for each row
var summary = {};
for (var row=1; row<table.length; row++) {
var key = table[row][0];
if (!(key in summary))
summary[key] = 0;
summary[key] += eval( formula );
}
// Append aggregated rows to results, and return
for (key in summary) {
summaryTable.push([key,summary[key]]);
}
// Sort the results
headers = summaryTable.splice(0, 1);
summaryTable.sort(colCompare).unshift(headers[0]);
return summaryTable;
}
EDIT: Nov 17 - added sort functionality
Assuming you have the names in Col A and the col A of the example is actually Col B,
in the summary sheet, try something like:
=ArrayFormula(query({'raw sheet'!A2:A,'raw sheet'!B2:B*'raw sheet'!C2:C}, "select Col1, sum(Col2) where Col1 <>'' group By Col1 label sum(Col2) 'TOTAL' "))