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()
Related
We have new entries being pulled into a Google sheet daily using XML (Sheet 1). We are using the code below to pull these entries into a separate Google sheet for end users (Sheet 2). The end users can then edit the data. We are trying to compare the ID numbers in Sheet 1 and Sheet 2. Each time we run the code, ID numbers from Sheet 1 that do not have a match in Sheet 2 should be added to Sheet 2. If the ID number in Sheet 1 already exists in Sheet 2, that ID number should be skipped. It isn't skipping the matches. Instead it is adding everything to Sheet 2 every time we run the code and Sheet 2 now contains duplicates.
for(var i = 1; i < slateDT.length; i ++) {
var bannerid = slateDT[i][0];
var match = "No Match";
var j = 1;
while(j < gradingDT.length && match == "No Match") {
var matchID = gradingDT[j][1].trim();
if(bannerid.trim() != matchID){
j++;
} else {
match = "Match";
}
}
if(match == "No Match"){
additions.push(moveColumns(slateDT[i]));
}
}
if(additions.length > 0) {
gradingSS.getRange(gradingDT.length + 1, 2, additions.length, additions[0].length).setValues(additions);
gradingDT = getDataValues(gradingSS.getName());
var sortRng = gradingSS.getRange(2, 1, gradingDT.length, gradingDT[0].length);
sortRng.sort(3);
}
function moveColumns(studentRow) {
studentRow.splice(17, 3);
var v = checkDefined(studentRow.splice(20, 1));
studentRow.splice(10, 0, v.join());
v = checkDefined(studentRow.splice(18, 1));
studentRow.splice(13, 0, v.join());
v = checkDefined(studentRow.splice(20));
studentRow.splice(14, 0, v.join());
return studentRow;
}
Ok, I'm assuming that your weird moveColumns function does what you want and that the column numbers mismatch per my question above. Replace your for loop with this:
for (var i = 0; i < slateDT.length; i++) {
var oldID = slateDT[i][0].trim();
var matchID = 0;
for (var j = 1; j < gradingDT.length; j++) {
var newID = gradingDT[j][1].trim();
if (oldID == newID) {
matchID = j;
break; //ends the j loop when it meets the match
}
} //for [j] loop
if (matchID == 0) {
additions.push(moveColumns(slateDT[i]));
Logger.log("No match was found for " + i);
} else {
Logger.log("A match was found for " + i + " at " + j);
}
} //for [i] loop
This is very similar to what you are trying to do with the while loop but without managing to never increment J under certain circumstances.
Once you are sure it is working comment out the two logger lines for performance.
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);
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];
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;
}
}
}
Fiddle 1
In this fiddle I have created 5 rectangle shapes on a single row using a for loop. Also I have set an interval between each iteration so that instead of directly displaying 5 rectangles together, they are displayed one by one separated by a small interval. This is one part of my solution. And it is working as intended. Now the problem arises in the next part.
What I actually want to do is create multiple rows and that the rectangle should be displayed one by one on first row and then in the same way on the next row. But there is some mistake in my code due to which instead of displaying one rectangle at a time, entire column is displayed at a time.
Here is the Second Fiddle
I hope you understood what I want to do here. How can I correct the code and get the desired result of displaying rectangle one by one and then advancing to the next row?
for (var i = 0; i < 3 ; i++) {
for (var j = 0; j < 5; j++) {
window.setTimeout(
(function (i,j){
return function() {
var box = paper.rect(j*100,i*50,100,50);
box.attr({fill:'yellow'});
}
})(i,j),j * 500)
}
}
I think that this solves your problem:
window.onload = function() {
var ROWS = 3,
COLS = 5;
function drawGrid(paper) {
for (var i = 0; i < ROWS; i += 1) {
drawRow(i, paper);
}
}
function drawRow(row, paper) {
for (var i = 0; i < COLS; i += 1) {
drawRect(row, i, paper);
}
}
function drawRect(row, col, paper) {
setTimeout(function () {
var box = paper.rect(col*100,row*50,100,50);
box.attr({fill:'yellow'});
}, (row * COLS * 1000) + (col * 1000));
}
drawGrid(Raphael(0, 0, 1920, 1000));
}
I just made a little refactoring and calculate the timeout depending on the current column and row.
Here is the fiddle: http://jsfiddle.net/dYRR2/6/
In other words the timeout should be i * 500 * 5 + j * 500 instead of j * 500.