I'm using ASGALLANT'S Hide/Show method shown here:
http://jsfiddle.net/asgallant/6gz2Q/
Except I have fourth data series called average. When a series is hidden or shown, I recalculate the average... by adjusting the fiddle above:
function showHideSeries () {
var sel = chart.getChart().getSelection();
var view = chart.getView() || {};
// if selection length is 0, we deselected an element
if (sel.length > 0) {
// if row is undefined, we clicked on the legend
if (sel[0].row == null) {
var col = sel[0].column;
if (typeof(columns[col]) == 'number') {
var src = columns[col];
// hide the data series
columns[col] = {
label: datatable.getColumnLabel(src),
type: datatable.getColumnType(src),
sourceColumn: src,
calc: function () {
return null;
}
};
//record as hidden
hiddenSeries[col] = true;
// grey out the legend entry
series[columnsMap[src]].color = '#CCCCCC';
chart.setOption('series', series);
//If Exists a columnLabel called "Average", check if last column (average column is currently shown), recalculate average
if ((datatable.getColumnLabel(datatable.getNumberOfColumns() - 1) == 'Average') && (hiddenSeries[datatable.getNumberOfColumns() - 1] == false)){
for(var r = 0; r < datatable.getNumberOfRows(); ++r) {
sum = 0;
k = 0
for(var c = 1; c < datatable.getNumberOfColumns()-1; ++c) {
if(hiddenSeries[c] == false){
if(datatable.getValue(r, c) > 0){
sum = sum + datatable.getValue(r, c);
k = k + 1;
}
}
}
if (k == 0) k = 1;
datatable.setValue(r, datatable.getNumberOfColumns() - 1, sum/k);
}
}
var tmpColumn = new Array();
// Add each data value to the array with push()
for(var i = 0; i < datatable.getNumberOfRows(); ++i) {
tmpColumn.push(datatable.getValue(i, col));
}
}
else {
var src = columns[col].sourceColumn;
// show the data series
columns[col] = src;
series[columnsMap[src]].color = null;
//record as shown
hiddenSeries[col] = false;
//If Exists a columnLabel called "Average", check if last column (average column is currently shown), recalculate average
if ((datatable.getColumnLabel(datatable.getNumberOfColumns() - 1) == 'Average') && (hiddenSeries[datatable.getNumberOfColumns() - 1] == false)){
for(var r = 0; r < datatable.getNumberOfRows(); ++r) {
sum = 0;
k = 0;
for(var c = 1; c < datatable.getNumberOfColumns() - 1; ++c) {
if(hiddenSeries[c] == false){
if(datatable.getValue(r, c) > 0){
sum = sum + datatable.getValue(r, c);
k = k + 1;
}
}
}
if (k == 0) k = 1;
datatable.setValue(r, datatable.getNumberOfColumns() - 1, sum/k);
}
}
//chart.setOption('series.' + i + '.type', 'bars');
if(datatable.getColumnLabel(src) == 'Average' || datatable.getColumnLabel(src) == 'Peak Demand [kVA]'){
chart.getOptions().series[columnsMap[src]].type = 'line';
if(c.second_axis == true){
chart.getOptions().series[columnsMap[src]].targetAxisIndex = 1;
}
}
}
chart.setDataTable(datatable);
view.columns = columns;
chart.setView(view);
google.visualization.events.addListener(chart, 'ready', function() {
document.getElementById('chartImg').href = chart.getChart().getImageURI();
});
chart.draw();
}
}
}
It works great, except that the Standard tooltip is not being updated on the re-draw of the chart. I would like to avoid using custom tooltips as a solution. I would think that the ToolTip values should automatically update on each draw of the chart... if not, there must be a way to force it?
The issue here is that I was using formatted numbers as shown below:
var formatter = new google.visualization.NumberFormat(
{negativeColor: 'red', negativeParens: true, pattern: '###,###'});
formatter.format(datatable, i);
Therefore, not only did I have to use datatable.setValue(row, column, value) but I also have to use datatable.setFormattedValue(row, column, *String* value).
Related
I have two separate scripts in google sheets that are triggered by "TRUE" value checkboxes. The first script is set to uncheck all checkboxes from the sheet and the second is resetting a dropdown list. I'll post the code below for the two scripts. Ultimately I would like both conditions to run on one checkbox. If the code can be written in a more simplified manor that would not be a bad thing.
Best regards, Jon
Script 1:
function onEdit(e) {
if (e.range.getSheet().getName() == 'Audits') {
if (e.range.getA1Notation() == 'J27' && e.value == "TRUE") {
e.range.setValue("FALSE");
var ss = SpreadsheetApp.getActive();
var sh = ss.getActiveSheet();
var rg = sh.getDataRange();
var vA = rg.getDataValidations();
var cbA = [];
for (var i = 0; i < vA.length; i++) {
for (var j = 0; j < vA[i].length; j++) {
var rule = vA[i][j];
if (rule != null) {
var criteria = rule.getCriteriaType();
if (criteria == SpreadsheetApp.DataValidationCriteria.CHECKBOX) {
sh.getRange(i + 1, j + 1).setValue(null)
}
}
}
}
}
}
}
Script 2
function onEdit(e) {
if (e.range.getSheet().getName() == 'Audits') {
if (e.range.getA1Notation() == 'E27' && e.value == "TRUE") {
e.range.setValue("FALSE");
var ss = SpreadsheetApp.getActive();
var sh = ss.getActiveSheet();
var rg = sh.getDataRange();
var vA = rg.getDataValidations();
var cbA = [];
for (var i = 0; i < vA.length; i++) {
for (var j = 0; j < vA[i].length; j++) {
var rule = vA[i][j];
if (rule != null) {
var criteria = rule.getCriteriaType();
if (criteria == SpreadsheetApp.DataValidationCriteria.VALUE_IN_LIST) {
sh.getRange(i + 1, j + 1).setValue(null)
}
}
}
}
}
}
}
To combine and simplify your script, I extracted the variable declarations, which are common to both scripts, outside of the if statements, and then directly added the conditional statement for the criteria of the second script to the first criteria using an or statement (||) as shown below:
function onEdit(e) {
var ss = SpreadsheetApp.getActive();
var sh = ss.getActiveSheet();
var rg = sh.getDataRange();
var vA = rg.getDataValidations();
if (e.range.getSheet().getName() == 'Audits') {
if (e.range.getA1Notation() == 'E27' && e.value == "TRUE") {
e.range.setValue("FALSE");
for (var i = 0; i < vA.length; i++) {
for (var j = 0; j < vA[i].length; j++) {
var rule = vA[i][j];
if (rule != null) {
var criteria = rule.getCriteriaType();
if (criteria == SpreadsheetApp.DataValidationCriteria.CHECKBOX || criteria == SpreadsheetApp.DataValidationCriteria.VALUE_IN_LIST) {
sh.getRange(i + 1, j + 1).setValue(null)
}
}
}
}
}
}
}
To test the script's functionality, I created the following setup:
When the checkbox in cell E27 is toggled, all of the sample dropdown lists and checkboxes are reset.
I hope this helps.
I'm gonna add some abilities to random blocks, u know that drops down when you hit a random block. Like you get 2 balls or bigger paddle or slower ball speed.
I need help with writing the for loop and if-statement that can add some abilities to my block levels. Like 1 random ability on level 1 and so on. So when you hit 1 random block of like 20 blocks, that has my ability. It would drop down to the paddle like the original breakout game.
I was thinking of a switch, if one random block with ability is hit and using that switch and randomize it.
void PowerUp()
{
powerups.Add(abilityballs_rect);
powerups.Add(abilitylong_rect);
powerups.Add(abilityslow_rect);
}
-
List<Rectangle> block = new List<Rectangle>();
List<Rectangle> block2 = new List<Rectangle>();
List<Rectangle> block3 = new List<Rectangle>();
List<Rectangle> block4 = new List<Rectangle>();
List<Rectangle> block5 = new List<Rectangle>();
List<Rectangle> block6 = new List<Rectangle>();
List<Rectangle> powerups = new List<Rectangle>();
-
if (level == 1)
{
if (block.Count == 14 && block2.Count == 14)
{
spriteBatch.DrawString(spritefont2, "LEVEL " + level, new Vector2(252, 400), Color.White);
}
foreach (Rectangle g in block)
{
spriteBatch.Draw(block_texture, g, Color.LimeGreen);
}
foreach (Rectangle r in block2)
{
spriteBatch.Draw(block_texture, r, Color.IndianRed);
}
}
else if (level == 2)
{
if (block3.Count == 18 && block4.Count == 27)
{
spriteBatch.DrawString(spritefont2, "LEVEL " + level, new Vector2(246, 400), Color.White);
}
foreach (Rectangle b in block3)
{
spriteBatch.Draw(block_texture, b, Color.CornflowerBlue);
}
foreach (Rectangle y in block4)
{
spriteBatch.Draw(block_texture, y, Color.Yellow);
}
}
else if (level == 3)
{
if (block5.Count == 36 && block6.Count == 18)
{
spriteBatch.DrawString(spritefont2, "LEVEL " + level, new Vector2(246, 400), Color.White);
}
foreach (Rectangle o in block5)
{
spriteBatch.Draw(block_texture, o, Color.Orange);
}
foreach (Rectangle p in block6)
{
spriteBatch.Draw(block_texture, p, Color.HotPink);
}
}
-
void AddBlocks()
{
//LEVEL 1
for (int i = 1; i < 3; i++)
{
for (int f = 1; f < 8; f++)
{
block.Add(new Rectangle((f * 63) + 94, (i * 40) + 60, block_texture.Width, block_texture.Height));
}
}
for (int i = 1; i < 3; i++)
{
for (int g = 1; g < 8; g++)
{
block2.Add(new Rectangle((g * 63) + 94, (i * 40) + 40, block_texture.Width, block_texture.Height));
}
}
//LEVEL 2
for (int i = 1; i < 3; i++)
{
for (int j = 1; j < 10; j++)
{
block3.Add(new Rectangle((j * 63) + 34, (i * 200) - 60, block_texture.Width, block_texture.Height));
}
}
for (int i = 1; i < 10; i++)
{
for (int k = 1; k < 4; k++)
{
block4.Add(new Rectangle((k * 103) + 143, (i * 20) + 140, block_texture.Width, block_texture.Height));
}
}
//LEVEL 3
for (int i = 1; i < 7; i++)
{
for (int j = 1; j < 7; j++)
{
block5.Add(new Rectangle((j * 63) + 127, (i * 20) + 190, block_texture.Width, block_texture.Height));
}
}
for (int i = 1; i < 10; i++)
{
for (int k = 1; k < 3; k++)
{
block6.Add(new Rectangle((k * 443) - 317, (i * 20) + 160, block_texture.Width, block_texture.Height));
}
}
}
-
void DeleteBlocks()
{
if (level == 1)
{
for (int j = 0; j < block.Count; j++)
{
if (ball_rect.Intersects(block[j]))
{
ball_speed.Y *= -1;
points += 1;
block.RemoveAt(j);
if (points > highscore)
{
highscore = points;
}
}
}
for (int k = 0; k < block2.Count; k++)
{
if (ball_rect.Intersects(block2[k]))
{
ball_speed.Y *= -1;
points += 1;
block2.RemoveAt(k);
if (points > highscore)
{
highscore = points;
}
}
}
if (block.Count == 0 && block2.Count == 0)
{
level++;
StartValueBallPaddle();
Start = false;
}
}
else if (level == 2)
{
for (int l = 0; l < block3.Count; l++)
{
if (ball_rect.Intersects(block3[l]))
{
ball_speed.Y *= -1;
points += 1;
block3.RemoveAt(l);
if (points > highscore)
{
highscore = points;
}
}
}
for (int m = 0; m < block4.Count; m++)
{
if (ball_rect.Intersects(block4[m]))
{
ball_speed.Y *= -1;
points += 1;
block4.RemoveAt(m);
if (points > highscore)
{
highscore = points;
}
}
}
if (block3.Count == 0 && block4.Count == 0)
{
level++;
StartValueBallPaddle();
Start = false;
}
}
else if (level == 3)
{
for (int n = 0; n < block5.Count; n++)
{
if (ball_rect.Intersects(block5[n]))
{
ball_speed.Y *= -1;
points += 1;
block5.RemoveAt(n);
if (points > highscore)
{
highscore = points;
}
}
}
for (int o = 0; o < block6.Count; o++)
{
if (ball_rect.Intersects(block6[o]))
{
ball_speed.Y *= -1;
points += 1;
block6.RemoveAt(o);
if (points > highscore)
{
highscore = points;
}
}
}
}
}
You might want to encapsulate your block data into a class, containing the color, the position and powerup of the block.
You could have a method inside that would be called "AddPowerup()" or something, this method will be used to add a random (or specific) powerup to the block. You'll maybe need a getter for your powerup.
Now in your game, you would hold all the blocks of a level inside a list, or a 2d array, or any collection that suits you
Then, to apply powerup to random blocks in the level, you could do something like this (mostly pseudo code, so not tested) :
List<Block> blockList = CreateBlockLevel(1);
int randomIndex = new Random().Next(blockList.Length-1);
blockList[randomIndex].AddPowerup();
If you want to apply a powerup to multiple blocks, you can put that in a for-loop, but then you might want to check if the block has already a powerup or not.
I'm extracting numbers from web page, and i want compare them, if
number is more than [previous one, then show it in iimDispaly(), otherwise if is it lower then skip, i trued here to do but cannot find a solution.
for(var i = 1; i <= 13; i++) {
for(var j = 3; j<=33; j+=3 ) {
iimPlayCode('TAG POS='+j+' TYPE=DIV ATTR=CLASS:"group_row_labeled" EXTRACT=TXT')
var res = iimGetLastExtract();
var result = res.replace(/[а-z]/g, '');
if(j==3) {
var firstRes = result;
}
if(result => firstRes) {
iimDisplay("Highest Number: " + result)
}
}
}
Copy & paste the following code and try to play it:
for(var i = 1; i <= 13; i++) {
for(var j = 3; j<=33; j+=3 ) {
iimPlayCode('TAG POS='+j+' TYPE=DIV ATTR=CLASS:"group_row_labeled" EXTRACT=TXT')
var res = iimGetLastExtract();
var result = parseFloat(res.replace(/[a-z]/g, ''));
if(j==3) {
var firstRes = result;
}
if(result >= firstRes) {
iimDisplay("Highest Number: " + result)
}
}
}
Given two strings (s1, s2), Levenshtein Distance is the minimum number of operations needed to change s1 to s2 or vice versa.
I want to show the result of changing s1 to s2. For example, changing Sunday to Saturday needs 3 operations. I need to show S++u+day. "+" is for each operations needed.
Here is a javascript snippet that returns what you want. If you are familiar with the dynamic programming algorithm you should be able follow this code. All the string operations/manipulation of the return string r and handling of min/curMin are what's changed from the original version.
function edits(t, s) {
var r = "";
if (s === t) {
return s;
}
var n = s.length, m = t.length;
if (n === 0 || m === 0) {
return "+".repeat(n + m);
}
var x, y, a, b, c, min = 0;
var p = new Array(n);
for (y = 0; y < n;)
p[y] = ++y;
for (x = 0; x < m; x++) {
var e = t.charCodeAt(x);
c = x;
b = x + 1;
var currMin = min;
min = n + 1;
for (y = 0; y < n; y++) {
a = p[y];
if (a < c || b < c) {
b = (a > b ? b + 1 : a + 1);
}
else {
if (e !== s.charCodeAt(y)) {
b = c + 1;
}
else {
b = c;
}
}
if (b < min) {
min = b;
}
p[y] = b;
c = a;
}
if (min > currMin) {
r += "+";
}
else {
r += t[x];
}
}
return r;
}
EDIT: The implementation above is an version optimized for speed and space so might be harder to read. The implemetation below is a modified version of the JS version from Wikipedia and should be easier to follow.
function getEditDistance(a, b) {
if(a.length === 0) return "+".repeat(b.length);
if(b.length === 0) return "+".repeat(a.length);
var matrix = [];
// increment along the first column of each row
var i;
for(i = 0; i <= b.length; i++){
matrix[i] = [i];
}
// increment each column in the first row
var j;
for(j = 0; j <= a.length; j++){
matrix[0][j] = j;
}
var r = "", min = 0;;
// Fill in the rest of the matrix
for(i = 1; i <= b.length; i++){
var currMin = min;
min = a.length + 1;
for(j = 1; j <= a.length; j++){
if(b.charAt(i-1) == a.charAt(j-1)){
matrix[i][j] = matrix[i-1][j-1];
} else {
matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
Math.min(matrix[i][j-1] + 1, // insertion
matrix[i-1][j] + 1)); // deletion
}
if (matrix[i][j] < min) {
min = matrix[i][j];
}
}
if (min > currMin) {
r += "+";
}
else {
r += b[i-1];
}
}
return r;
}
EDIT2: Added explanation of the algorithm and example output
Below is the levenshtein matrix from the input strings "kitten" and "sitting". What I changed in the original algorithm is that I added a check if the current rows minimum value is larger then the previous rows minimum, and if it is, there is an edit in the current row and thus adding a "+". If not and the " edit cost" for the current row is the same as the previous. Then there is no edit necessary and we just add the current character to the result string. You can follow the algorithm row by row in the image (starting at row 1, and column 1)
Examples:
> getEditDistance("kitten", "sitting");
'+itt+n+'
> getEditDistance("Sunday", "Saturday");
'S++u+day'
Having the following map:
function (doc, meta) {
emit(dateToArray(doc.timestamp), doc.user_id);
}
What changes to I need on this reduce to get a count of user_ids that occur more than once?
function(key, values, rereduce) {
var counts = {}, id;
for (var i = 0; i < values.length; i++) {
id = values[i].replace('-', '_');
counts[id] = counts[id] || 0;
counts[id] = counts[id] + 1;
}
var total = 0;
for (id in counts) {
if (counts[id] > 1) total++;
}
return total;
}
I tried it on plain JS passing some array of values, and it works as I expected. Debugging is difficult on Couchbase though (or I don't know how), and I'm not seeing any errors on the error.1 and mapreduce_errors.1 files.
Update: I've been doing some crude debugging, going line by line, and this is what I have so far:
function(key, values, rereduce) {
var counts = {}, total = 0;
for (var i = 0; i < values.length; i++) {
var id = values[i];
if (id.indexOf('id_') != 0) id = 'id_' + values[i].replace(/-/g, '_');
return id;
counts[id] = counts[id] || 0;
counts[id] = counts[id] + 1;
}
for (id in counts) {
if (counts[id] > 1) total++;
}
return total;
}
Notice the return id; line. It returns a modified uuid, prefixed by "id_", just like you'd expect by looking at the code.
But then, if I change that line to return counts; or return total;, the result from Couchbase is "The results of this view is empty." What gives?
It was one painful debugging session, but here's the final solution:
function(key, values, rereduce) {
var counts = {}, total = 0, id;
if (rereduce) {
for (var set in values) {
for (var i in values[set]) {
counts[i] = (counts[i] || 0) + values[set][i];
}
}
for (f in counts) {
if (counts[f] > 1) total++;
}
return total;
} else {
for (var i in values) {
id = 'id_' + values[i].replace(/-/g, '_');
counts[id] = (counts[id] || 0) + 1;
}
return counts;
}
}
An extra lesson learned: Couchbase won't return big objects off of a reduce operation. Gotta learn more about that.