RaphaelJS create rectangle using for loop with interval - for-loop

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.

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()

How to correct the code such that the loop stops when there is a data match

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.

Hash a Set of Integers from a Domain into a Set of Buckets

Say I have a set of integers ranging between 1-100. I will only have 5 of these integers drawn out of a hat. I want to then take those 5 integers and place them into 5 buckets guaranteed unique (without having to deduplicate or anything using something like quadratic probing). Wondering how to do that.
For example, say I have these numbers (random from 1-100):
1 5 20 50 100
I then want to take those numbers and place them into these 5 buckets:
a b c d e
Using some hash function to accomplish it. For example, perhaps like this:
hash(1) -> b
hash(5) -> a
hash(20) -> e
hash(50) -> d
hash(100) -> c
Wondering how to write the hash function so that it takes a number x from a domain of numbers D and a set of numbers D(X) from that domain, and outputs 1 bucket b from the set of buckets B.
H : D(X) -> B
Next time around I might have 6 numbers between 1 and 1,000, going into 6 buckets. So then I would need a new hash function that works using those constraints (6 numbers, 6 buckets, range 1-1,000).
The goal is as few steps as possible.
Note: The hash function for this example won't take integers in a domain larger than 10,000 lets say, as well as the size of the set of integers limited to some small number too like 1,000.
Update
Basically I am trying to get this to happen:
// var domain = [1, 2, ..., 100]
// var set = [1, 5, 20, 50, 100]
// var buckets = [1, 2, 3, 4, 5]
hash(1) // 2
hash(5) // 1
hash(20) // 5
hash(50) // 4
hash(100) // 3
function hash(integer) {
if (integer == 1) return 2
if (integer == 5) return 1
if (integer == 20) return 5
if (integer == 50) return 4
if (integer == 100) return 3
}
But I don't know how to construct that hash function dynamically.
One solution (in JavaScript) would be to just create a map like this:
var map = {
1: 2,
5: 1,
20: 5,
50: 4,
100: 3
}
But that's sort of cheating because the object in JavaScript is implemented as a hashtable underneath (or something like that). So I am looking for how to do this at a low level, just using basically what assembly gives you.
Pretty much, I want to do this:
1
5 |
| | 20
| | 50 |
| | 100 | |
[ slot1, slot2, slot3, slot4, slot5 ]
Where 1 is somehow "hashed" to go into that slot2 in an array of size 5 (that slot is arbitrary for this example), etc.
Suppose the domain of your integer values is the range from 0 to n-1, and you want the set of values [x0, x1, ..., xk-1] to map to values from 0 to k-1.
Create an array of n values containing the numbers from 0 to k-1 in roughly equal amounts, for example [a0 = 0, a1 = 1, ..., ak = 0, ..., an = n%k].
Then for each of the k values in the initial set (xi, where i = 0 .. k-1), change the k-th element of this array to i, either by direct assignment or by swapping with a value from elsewhere (taking care not to clobber a value set for a previous element of the initial set).
Then to hash a value y, just fetch the y-th value from this array.
DEMO
Here's a Javascript demo that basically implements the above algorithm, except that instead of pre-filling the array with values from 0 to k-1, it first inserts the hash values for the selected items, then fills the remaining items with the repeating sequence of numbers from 0 to k-1. You will probably get better collision resistance by using a random sequence instead of incrementing values, but I hope you get the picture.
var hash_array;
function generate_hash() {
var i, j, k;
var v = document.getElementById;
var n = document.getElementById("n").value;
// Create a new hash lookup table
hash_array = Array(n);
// Initialize every value to -1
for (i=0; i<n; i++) hash_array[i] = -1;
// Map the given values to the first k hash buckets
var initial_values = document.getElementById("init").value.split(/ +/);
k = initial_values.length;
for (i=0; i<k; i++) {
hash_array[initial_values[i]] = i;
}
// Fill the remaining buckets with values from 0 to k-1
// This could be done by selecting values randomly, but
// here we're just cycling through the values from 0 to k-1
for (i=j=0; i<hash_array.length; i++) {
if (hash_array[i] == -1) {
hash_array[i] = j;
j = (j + 1) % k;
}
}
document.getElementById("gen").innerHTML = "Hash lookup table:<br>" + hash_array.join(", ");
}
<h2>Demo</h2>
<p>Creating a hash function that works on integer values less than <i>n</i>. What is the value of <i>n</i>?<br>
<input type="number" id="n" min="6" max="100" value="20"/></p>
<p>Enter a few different values separated by spaces. These will hash to the first buckets<br/>
<input type="text" size="40" id="init" value="2 3 5 6 9"/></p>
<p id="gen"><button onclick="generate_hash(); return false">Generate hash table</button></p>
Something like this should work:
Create a set of bucket IDs and populate it ahead of hashing (assumption here is that set guarantees uniqueness). This means that you have to know in advance how many buckets you want.
For each element from the input set calculate hash(element) modulo bucketIds.size to find index of the next ID to use.
Remove the resulting bucket ID from the set of bucked IDs
Repeat (until you are done or the set of IDs is exhausted)
Feel free to inspect the noddy implementation in JS using arrays (Node8).
If you'd like a function that's not a straight map, you could also experiment with Polynomial Regression.
Here's a JavaScript example using some free code under the GNU license.
/***************************************************************************
* Copyright (C) 2018 by Paul Lutus *
* lutusp#arachnoid.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
// classic Gauss-Jordan matrix manipulation functions
var gj = gj || {}
gj.divide = function(A, i, j, m) {
for (var q = j + 1; q < m; q++) {
A[i][q] /= A[i][j];
}
A[i][j] = 1;
}
gj.eliminate = function(A, i, j, n, m) {
for (var k = 0; k < n; k++) {
if (k != i && A[k][j] != 0) {
for (var q = j + 1; q < m; q++) {
A[k][q] -= A[k][j] * A[i][q];
}
A[k][j] = 0;
}
}
}
gj.echelonize = function(A) {
var n = A.length;
var m = A[0].length;
var i = 0;
var j = 0;
var k;
var swap;
while (i < n && j < m) {
//look for non-zero entries in col j at or below row i
k = i;
while (k < n && A[k][j] == 0) {
k++;
}
// if an entry is found at row k
if (k < n) {
// if k is not i, then swap row i with row k
if (k != i) {
swap = A[i];
A[i] = A[k];
A[k] = swap;
}
// if A[i][j] is != 1, divide row i by A[i][j]
if (A[i][j] != 1) {
gj.divide(A, i, j, m);
}
// eliminate all other non-zero entries
gj.eliminate(A, i, j, n, m);
i++;
}
j++;
}
}
// a simple data class
function Pair(x,y) {
this.x = x;
this.y = y;
};
Pair.prototype.toString = function() {return x + ',' + y};
// matrix functions
var matf = matf || {}
// a weak substitue for printf()
matf.number_format = function(n,p,w) {
s = n.toExponential(p);
while(s.length < w) {
s = ' ' + s;
}
return s;
}
// produce a single y result for a given x
matf.regress = function(x, terms) {
var y = 0;
var m = 1;
for (var i = 0; i < terms.length;i++) {
y += terms[i] * m;
m *= x;
}
return y;
}
// compute correlation coefficient
matf.corr_coeff = function(data, terms) {
var r = 0;
var n = data.length;
var sx = 0;
var sx2 = 0, sy = 0, sy2 = 0, sxy = 0;
var x, y;
for (var i = 0;i < data.length;i++) {
pr = data[i];
var x = matf.regress(pr.x, terms);
var y = pr.y;
sx += x;
sy += y;
sxy += x * y;
sx2 += x * x;
sy2 += y * y;
}
var div = Math.sqrt((sx2 - (sx * sx) / n) * (sy2 - (sy * sy) / n));
if (div != 0) {
r = Math.pow((sxy - (sx * sy) / n) / div, 2);
}
return r;
}
// compute standard error
matf.std_error = function(data, terms) {
var r = 0;
var n = data.length;
if (n > 2) {
var a = 0;
for (var i = 0;i < data.length;i++) {
pr = data[i];
a += Math.pow((matf.regress(pr.x, terms) - pr.y), 2);
}
r = Math.sqrt(a / (n - 2));
}
return r;
}
// create regression coefficients
// for provided data set
// data = pair array
// p = polynomial degree
matf.compute_coefficients = function(data, p) {
p += 1;
var n = data.length;
var r, c;
var rs = 2 * p - 1;
//
// by request: read each datum only once
// not the most efficient processing method
// but required if the data set is huge
//
// create square matrix with added RH column
m = Array();
for (var i = 0; i < p; i++) {
mm = Array();
for (var j = 0; j <= p; j++) {
mm[j] = 0;
}
m[i] = mm;
}
//double[][] m = new double[p][p + 1];
// create array of precalculated matrix data
mpc = Array();
for(var i = 0;i < rs;i++) {
mpc[i] = 0;
}
mpc[0] = n;
for (var i = 0;i < data.length;i++) {
pr = data[i];
// process precalculation array
for (r = 1; r < rs; r++) {
mpc[r] += Math.pow(pr.x, r);
}
// process RH column cells
m[0][p] += pr.y;
for (r = 1; r < p; r++) {
m[r][p] += Math.pow(pr.x, r) * pr.y;
}
}
// populate square matrix section
for (r = 0; r < p; r++) {
for (c = 0; c < p; c++) {
m[r][c] = mpc[r + c];
}
}
// reduce matrix
gj.echelonize(m);
// extract result column
terms = Array();
for (var i = 0;i < m.length;i++) {
mc = m[i];
terms[i] = mc[p];
}
return terms;
}
// test the system using known data
matf.test = function() {
var xd = [-1,0,1,2,3,5,7,9];
var yd = [-1,3,2.5,5,4,2,5,4];
data = Array();
for(var i = 0;i < xd.length;i++) {
data[i] = new Pair(xd[i],yd[i]);
}
terms = compute_coefficients(data,6);
var prec = 16;
var width = 24;
for(var i = 0;i < terms.length;i++) {
print(number_format(terms[i],prec,width) + ' * x^' + i);
}
cc = corr_coeff(data,terms);
print ('cc = ' + number_format(cc,prec,width));
se = std_error(data,terms);
print('se = ' + number_format(se,prec,width));
}
//test();
// "data" is an array of Pair(x,y) data
// p = polynomial degree
matf.process_data = function(data,p) {
var terms = matf.compute_coefficients(data,p);
var cc = matf.corr_coeff(data,terms);
var se = matf.std_error(data,terms);
return [terms,cc,se];
}
/**** END Paul Lutus' code ****/
function f(cs, x){
let n = cs.length - 1;
let result = 0;
for (let i=0; i<cs.length; i++)
result += cs[i] * Math.pow(x, i);
return result;
}
var data = [[1,1], [5,2], [20,3], [50,4], [100,5]];
var xy_data = []
for (let i of data)
xy_data.push(new Pair(i[0], i[1]));
var result = matf.process_data(xy_data, xy_data.length - 1);
for (let i=0; i<data.length; i++)
console.log(data[i][0], f(result[0], data[i][0]));

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) {

Make each event fit the bounding box by date

Taking the example from here
I want to make the events look like this:
instead of how it is on the page:
Notice how there is a space between the event and the bounding box.
It seems the element style is auto generated by the scheduler and calculates a width. How would I go about widening the event to fit exactly in the bounding box?
I found the answer on another stackedoverflow question.
This is her code diving by columns.length instead of 2
//Override kendo function for deciding events with
kendo.ui.MultiDayView.fn._arrangeColumns = function (element, top, height, slotRange) {
var startSlot = slotRange.start;
element = { element: element, slotIndex: startSlot.index, start: top, end: top + height };
var columns,
slotWidth = startSlot.clientWidth,
eventRightOffset = slotWidth * 0.10,
columnEvents,
eventElements = slotRange.events(),
slotEvents = kendo.ui.SchedulerView.collidingEvents(eventElements, element.start, element.end);
slotRange.addEvent(element);
slotEvents.push(element);
columns = kendo.ui.SchedulerView.createColumns(slotEvents);
//This is where the magic happens
var columnWidth = slotWidth / columns.length;
//Original code: var columnWidth = (slotWidth - eventRightOffset) / columns.length;
//This is where the magic ends
for (var idx = 0, length = columns.length; idx < length; idx++) {
columnEvents = columns[idx].events;
for (var j = 0, eventLength = columnEvents.length; j < eventLength; j++) {
columnEvents[j].element[0].style.width = columnWidth - 4 + "px";
columnEvents[j].element[0].style.left = (this._isRtl ? this._scrollbarOffset(eventRightOffset) : 0) + startSlot.offsetLeft + idx * columnWidth + 2 + "px";
}
}
};

Resources