Looping through Google Sheet - for-loop

I am having a hard time with Ranges and figuring out why I need to add i+1 in getRange to get the correct cell in my loop. I'm not sure if its because I increment before the variable or if it has something to do with the initial value?
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
var range = sheet.getDataRange();
var values = range.getValues();`
for (var i = 3; i < 30; ++i) {
var row = values[i];
var msgSent = row[3];
sheet.getRange(i + 1, 4).setValue("ALERTED");
Any help is appreciated!

.getRange() just uses integer based referencing while the 2D array of values needs to be referenced with a zero-based approach.
The 1st row (array) in values is values[0], but .getRange() uses .getRange(1, 1) to reference the 1st row, 1st column of the sheet. The 1st column value in the 1st row would be values[0][0].
Now if you're looping through 30 or so rows and want to set the values all at once, which is much better than setting them one-by-one, it'll look some thing like this...
var s = ss.getSheetByName('Sheet1');
var newValues = [];
for (var i = 3; i < 30; ++i) {
var row = values[i];
var msgSent = row[3];
newValues.push(["ALERTED"]);
}
s.getRange(4, 4).offset(0, 0, newValues.length).setValues(newValues);

Related

Dropdown list apply more than edited row in appscript

First I am so sorry for my English Skill I have to use my poor English Skill to make this question.
I made a script to apply Depended Data Validation on Column E of Orders Sheet when new SKU added to Column D, like when I add 1 SKU to D2, E2 will show dropdown list with all Supplier that SKU have (ex: SKU00124 have Amazon, Walmart, Chewy as supplier if I input SKU00124 to D2, E2 will show dropdown list with Amazon, Walmart, Chewy option for user choice, and SKU00122 have only Walmart, Amazon when I input SKU00122 to D3, E3 will show Walmart, Amazon). SKU and Supplier Store at Item_List sheet with SKU at column B and supplier at Column E.
My issue is in Orders sheet column D where to store SKU, it get data from Draft_Order column AA, I am using a formula to get values of AA input to D.
={"SKU";ARRAYFORMULA(if(B2:B="","",'Draft_Order'!AA2:AA))}
I see the script does not work correctly, I have to delete values in column D and when the formula gets values again the script runs. But it not set Data Validation to all range Ex: If SKU input from D2:D20, it not apply data validation to E2:E20, script just applies it row by row and only apply for 5 row (means only E2:E7 applied data validation), if I want more I have to delete the rest of SKU.
I use onEdit(e) to catch cell edited on column D
Here is my example spreadsheet: My Spreadsheet here
Here is my script:
function onEdit(e) {
// Get the active sheet
var sheet = e.source.getActiveSheet();
// Check if the active sheet is the "Order" sheet
if (sheet.getSheetName() == "Order") {
// Get the range that was edited
var range = e.range;
// Get the column of the edited cell
var col = range.getColumn();
// Check if the edited cell is in column D
if (col == 4) {
// Get the values in the edited range
var values = range.getValues();
// Loop through the values in the range
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[i].length; j++) {
// Check if the value is not empty
if (values[i][j] != "") {
// Get the SKU
var sku = values[i][j];
// Get the sources for the SKU using the getSources function
var sources = getSources(sku);
// Check if there are any sources for the SKU
if (sources.length > 0) {
// Calculate the row and column of the current cell
var row = range.getRow() + i;
var col = range.getColumn() + 1;
// Get the cell in column E
var cell = sheet.getRange(row, col);
// Set the data validation for the cell using the sources list
cell.setDataValidation(SpreadsheetApp.newDataValidation().setAllowInvalid(true).requireValueInList(sources).build());
}
}
}
}
}
}
}
function getSources(sku) {
// Get the active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Get the "Item List sheet
var sheet = ss.getSheetByName("Item List");
// Get the data in the sheet
var data = sheet.getDataRange().getValues();
// Create an array to store the sources
var sources = [];
// Loop through the data in the sheet
for (var i = 0; i < data.length; i++) {
// Check if the SKU in column B matches the input SKU
if (data[i][1] == sku) {
// Add the source in column E to the sources array
sources.push(data[i][4]);
}
}
// Return the sources array
return sources;
}
I am trying to fix it by using a range define and apply validation for range length by using edit values.length but script applies data validation for a range more than SKU range.
Looks like if SKU values from D2:D20, the dropdown list will show on E2:E35 when D21:D35 is blank and does not have any SKU
and issue I got in first script still meets (only E2:E7 is correct values of data validation, if I need more I have to delete it again.
Here is my try:
function onEdit(e) {
var sheet = e.source.getActiveSheet();
if (sheet.getSheetName() == "Order") {
var range = e.range;
var col = range.getColumn();
if (col == 4) {
var values = range.getValues();
Logger.log("values.length: " + values.length);
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[i].length; j++) {
if (values[i][j] != "") {
var sku = values[i][j];
var sources = getSources(sku);
if (sources.length > 0) {
var row = range.getRow() + i;
var col = range.getColumn() + 1;
var dataValidation = SpreadsheetApp.newDataValidation().setAllowInvalid(true).requireValueInList(sources).build();
// Apply data validation to the range of cells in column E
var numRows = values.length;
for (var k = 0; k < numRows; k++) {
if (values[k][0] == "") {
numRows--;
}
}
var validationRange = sheet.getRange(row, col, numRows, 1);
validationRange.clearDataValidations().clearContent();
validationRange.setDataValidation(dataValidation);
Logger.log("numRows: " + numRows);
}
}
}
}
}
}
}
As you see, I try to Logger.log numRow and value.lenght but after script run nothing show
please help me:
1/ script will run each time when I input SKU to AA column at Draft_Orders sheet
2/ Script will run when I input many SKU in 1 times like more than 100 SKU
3/ Script will apply to correct range (SKU have in D2:D100 E2:E100 have data validation not E2:E250
The onEdit() trigger works when the user edits the range. In this case you need to validate when edit has been done in the main sheet where you're getting the data from. You will need to validate whenever the user edits column AA in sheet Draft Order. To give you an idea, check this script (make sure the ranges and sheet name matches with yours):
function onEdit(e) {
var sheet = e.source.getActiveSheet();
if (sheet.getSheetName() == "Draft Order") {
var range = e.range;
var col = range.getColumn();
if (col == 27) {
var values = range.getValues();
Logger.log("values.length: " + values.length);
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[i].length; j++) {
if (values[i][j] != "") {
var sku = values[i][j];
var sources = getSources(sku);
if (sources.length > 0) {
var row = range.getRow() + i;
var col = range.getColumn() + 1;
var dataValidation = SpreadsheetApp.newDataValidation().setAllowInvalid(true).requireValueInList(sources).build();
// Apply data validation to the range of cells in column E
var numRows = values.length;
for (var k = 0; k < numRows; k++) {
if (values[k][0] == "") {
numRows--;
}
}
var validationRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Orders").getRange(row, 5);
validationRange.clearDataValidations().clearContent();
validationRange.setDataValidation(dataValidation);
Logger.log("numRows: " + numRows);
}
}
}
}
}
}
}
References:
onEdit()

refactor d3 from tsv to json and replace type function

I need to factor out the type function so I can use a json variable instead of tsv; I am already doing a data.forEach() to parse the date so that's OK. I haven't been able to figure out what this does:
for (var i = 1, n = columns.length; i < n; ++i) d[columns[i]] = d[columns[i]]
/ 100;
in
function type(d, i, columns) {
d.date = parseDate(d.date);
for (var i = 1, n = columns.length; i < n; ++i) d[columns[i]] = d[columns[i]]
/ 100;
return d;
}
this is the example:
Stacked bar
This function just normalizes values.
you can see that each row's sum is 100. So, this function divides each one to 100 and result will be between 0 and 1.
This function plays middleware role, it will be applied, before you start using loaded tsv data.
So, if you want to use json, you can skip this function and make values in json 100 times smaller. Or you can keep this func and just change from
d3.tsv("data.tsv", type, function(error, data) {
to
d3.json("your-json-file-url.json", type, function(error, data) {

How to set a number string into a real string formation?

I'm now using NPOI to cope with Excel export, and here's my codes (part in .NET):
int rowIndex = 1;
for (int i = 0; i < dt.Rows.Count; i++)
{
IRow dataRow = sheet.CreateRow(rowIndex);
for (int j = 0; j < cellCount; j++)
{
cell = dataRow.CreateCell(j,CellType.String);
cell.SetCellValue(new HSSFRichTextString(dt.Rows[i][j].ToString()));
}
rowIndex++;
}
What makes me feel surprised is there's a list whose number string is "20150525", and it will be analyzed as "2015……E+10" formation (scientific number formation). However I wanna keep it as a string value. So How?
Thanks!
In fact we have to set a CellStyle, snippet of sample codes is below:
IRow row = book[0].CreateRow(rowIndex + 1);
ICell rowCell = null;
rowCell = row.CreateCell(colIndex);
rowCell.SetCellValue(realCellValue);
ICellStyle cellStyle = book.CreateCellStyle();
cellStyle.DataFormat = HSSFDataFormat.GetBuiltinFormat("#");
rowCell.CellStyle = cellStyle;

actionscript 2: pick 3 random nummers from array

i need 3 random variables from array 0-36 (roulette).
i have this code. script return first 3 nummers from random array.
i need return 3 random nummers (from random position) from random array.
help me please.
onClipEvent (load) {
// field 0-36
var rands = [];
for (var i = 0; i < 36; ++i)
{
rands[i] = i;
}
// random sorting array
rands.sort(function(a,b) {return random(3)-1;});
// final variables 1,2,3
random1 = rands[0];
random2 = rands[1];
random3 = rands[2];
}
this is possible code for 1 variable, i need convert this to 3 variables in AS2
n = 3;
for (var i:Number = 0; i < n; i++) {
var randomSelection = Math.floor((Math.random() * rands.length));
trace("Selected: " + rands[randomSelection]);
}
It's not 100% clear from your question what you want, but this is my guess:
var n = 3;
var random = [];
for (var i:Number = 0; i < n; i++) {
// Store the random selections in an array
random.push(Math.floor(Math.random() * rands.length));
}
// You could assign them to variables or access directly via array
var random1 = random[0];
var random2 = random[1];
var random3 = random[2];

how to get screen coords using projection with a multipolygon in d3

I am using D3 to display customer location within counties. For MOST of my counties I am not having any problems. I am using the following code snippet to get the XY coordinates for my counties
var countyCoords = county.geometry.coordinates[0];
...
for (var j = 0; j < countyCoords.length; j++)
{
var x = projection(countyCoords[j])[0];
var y = projection(countyCoords[j])[1];
Works like a charm, with one exception. If my county.geometry["type"] is a MultiPolygon, these lines fail. How do I get the screen locations (i.e. x,y) of a MultiPolygon in d3?
Here are some lines from my input to d3.json() [edited for display here]
"counties":
{
"type":"GeometryCollection",
"geometries":[
{"type":"MultiPolygon","properties":{"NAME":"Chester"},"id":"Chester","arcs":[[[13,2111,15,2112,17,18,2113,2114,20,2115,2116,-1951,2117,2118,2119,9,2120,11,2121]],[[2122,2123]]]},
{"type":"Polygon","properties":{"NAME":"Clarion"},"id":"Clarion","arcs":[[-1966,2124,-2004,-2018,-2063]]},
I get the counties by using this line:
counties = topojson.feature(topoData, topoData.objects.counties);
I iterate through the counties and collect the XY locations mentioned above like this:
for (var i = 0; i < counties.features.length; i++)
{
var county = counties.features[i];
var countyCoords = county.geometry.coordinates[0];
var xy = [];
for (var j = 0; j < countyCoords.length; j++)
{
var x = projection(countyCoords[j])[0];
var y = projection(countyCoords[j])[1];
var dataPoint = [x,y];
xy.push(dataPoint);
}
county.properties["XY"] = xy;
}
This works correctly for the county "Clarion" but fails for the county "Chester".
A GeoJSON multipolygon has an additional array level to its structure that you'd need to iterate through:
http://geojson.org/geojson-spec.html
To account for this, you'd have to do something like this:
if (county.geometry.type == "MultiPolygon") {
for (var x=0; x< countyCoords.length;x++) {
for (var j = 0; j < countyCoords[x].length; j++) {
var x = projection(countyCoords[x][j])[0];
var y = projection(countyCoords[x][j])[1];
Here is the code to solve my problem. It is pretty much the suggestion of Elijah but tailored to my specific needs. (Note: this is only for Multipolygon types, normal polygon types should use the code identified in my original post). Also, please feel free to point out anywhere where my code can be improved.
for (var i = 0; i < counties.features.length; i++)
{
var county = counties.features[i];
if (county.geometry["type"] === "MultiPolygon")
{
var xy = [];
for (var k = 0; k < county.geometry.coordinates.length; k++)
{
var countyCoords = county.geometry.coordinates[k][0];
for (var j = 0; j < countyCoords.length; j++)
{
var x = projection(countyCoords[j])[0];
var y = projection(countyCoords[j])[1];
var dataPoint = [x,y];
xy.push(dataPoint);
}
county.properties["XY"] = xy;
}
}
}

Resources