So I started editing this code so I can make it create words. Eventually making a replication of Alan Turing's Bombe. However after my last edit my "assignment" became invalid? Now I am confused, especially since I am not 100% familiar with google-apps-script.
Here is what my current script currently says:
function bombeCode2() {
var fastRotor = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
"O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
];
var mediumRotor = fastRotor;
var slowRotor = fastRotor;
var rows = 26;
var columns = 26;
for (var i = 0; i < rows; i++) {
Logger.log('Outer Loop: value of i : ' + i);
// Logger.log("Partition for Outer Loop");
// Logger.log(" ");
var fastRotorValue = fastRotor[i];
for (var j = 0; j < columns; j++) {
Logger.log('-Inner Loop value of j : ' + j);
//var fastRotorValue = fastRotor[i];
var medRotorValue = mediumRotor[j];
// Logger.log("---- " + fastRotorValue + " " + medRotorValue);
for (var k = 0; k < 26; k++) {
// Logger.log('---- XXXX Third Loop value of k : ' + k);
//var fastRotorValue = fastRotor[i];
//var medRotorValue = mediumRotor[j];
var slowRotorValue = slowRotor[k];
if ("---- XXXX " + fastRotorValue + " " + medRotorValue + " " + slowRotorValue = "WEATHER") {
Logger.log("---- XXXX " + fastRotorValue + " " + medRotorValue + " " + slowRotorValue = "WEATHER")
}
// Logger.log("---- XXXX " + fastRotorValue + " " + medRotorValue + " " + slowRotorValue);
};
//var objectNumberValuePair = {"0":"A", "1":"B", "2":"C","3":"D","4":"E","5":"F","6":"G","7":"H","8":"I",
// "9":"J","10":"K","11":"L","12":"M","13":"N","14":"O","15":"P","16":"Q","17":"R",
// "18":"S","19":"T","20":"U","21":"V","22":"W","23":"X","24":"Y","25":"Z"}
// Logger.log(slowRotorValue = objectNumberValuePair);
// Logger.log(medRoterValue = objectNumberValuePair);
// Logger.log(fastRoterValue = objectNumberValuePair);
}
}
}
Yes I know that half of it is useless as is. However this is only so I can remember what everything is/does.
Does anyone know how to fix this "assignment?" Here is the error message it gives me "Invalid assignment left-hand side. (line 1, file "B. Test 2")."
You are doing a variable assignment in an if-statement.
Try using valid js-syntax:
var compareValue = "---- XXXX " + fastRotorValue + " " + medRotorValue + " " + slowRotorValue;
if (compareValue === "WEATHER"){
Logger.log(compareValue + ' === "WEATHER"');
}
Related
I have noticed that the script is significantly faster when I comment out the line that calls setValues(). Not that it's a super big issue, but I am interested in the different ways (if any) that I could speed the process up. If anyone has some insight, I would greatly appreciate it! It would also help me see where I could make similar optimizations in other parts of my script.
Here is the code in question:
// import new value(s)
if ((numRaw - numDk) > 0) {
var iDate;
var lastRow = getLastDataRow(earSheet, columnToLetter(earSheet.getRange("EAR_DATES_RNG").getColumn()));
var distName = impSheet.getRange("A1").getValue().toString();
var defaultDept = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("All Lists").getRange("A2").getValue().toString();
impData.forEach(function (entry) {
iDate = new Date(entry[0]);
var newEar = earSheet.getRange("A" + lastRow + ":G" + lastRow).getValues(); // init copy
if (currData.get(iDate.getTime()) == null) {
newEar[0][0] = entry[0];
newEar[0][1] = distName;
newEar[0][6] = entry[1];
newEar[0][3] = "Royalties";
newEar[0][4] = defaultDept;
newEar[0][5] = "NOT SPECIFIED";
earSheet.getRange("A" + lastRow + ":G" + lastRow).setValues(newEar);
Logger.log("ADDED: " + entry[1] + " to row: " + lastRow);
addedNewData++;
lastRow++;
}
else {
Logger.log("EXISTING DATA FOUND: " + currData.get(iDate.getTime()));
}
});
}
Using Matt and Cooper's suggestions, I came up with this revised code which is significantly faster:
if ((numRaw - numDk) > 0) {
var iDate;
var prevLastRow = getLastDataRow(earSheet, columnToLetter(earSheet.getRange("EAR_DATES_RNG").getColumn()));
var currLastRow = getLastDataRow(earSheet, columnToLetter(earSheet.getRange("EAR_DATES_RNG").getColumn()));
var rangeSize = numRaw;
var distName = impSheet.getRange("A1").getValue().toString();
var defaultDept = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("All Lists").getRange("A2").getValue().toString();
var newEar = earSheet.getRange("A" + prevLastRow + ":G" + (prevLastRow + rangeSize - 1)).getValues(); // init copy
impData.forEach(function (entry) {
iDate = new Date(entry[0]);
if (currData.get(iDate.getTime()) == null) {
newEar[currLastRow-2][0] = entry[0];
newEar[currLastRow-2][1] = distName;
newEar[currLastRow-2][6] = entry[1];
newEar[currLastRow-2][3] = "Royalties";
newEar[currLastRow-2][4] = defaultDept;
newEar[currLastRow-2][5] = "NOT SPECIFIED";
Logger.log("ADDED: " + entry[1] + " to row: " + currLastRow);
addedNewData++;
currLastRow++;
}
else {
Logger.log("EXISTING DATA FOUND: " + currData.get(iDate.getTime()));
}
});
earSheet.getRange("A" + prevLastRow + ":G" + (prevLastRow + rangeSize - 1)).setValues(newEar);
}```
I have a chart with two data sets and I am trying to get the min/max values for both data sets but for the moment I don't even get for any of the data sets!
With the same principle of code for one data set graph it work perfectly.
See my code for two data set graph:
window.onload = function() {
// first data set for PM2.5
var $dataPointsP2m5 = <?php echo json_encode($dataPoints_p2m5, JSON_PRETTY_PRINT | JSON_PRESERVE_ZERO_FRACTION | JSON_NUMERIC_CHECK); ?>;
// second data set for PM1.0
var $dataPointsP1m0 = <?php echo json_encode($dataPoints_p1m0, JSON_PRETTY_PRINT | JSON_PRESERVE_ZERO_FRACTION | JSON_NUMERIC_CHECK); ?>;
var chartPm = new CanvasJS.Chart("chartContainer_pm", {
zoomEnabled: true,
theme: "light2",
title: {
text: "Air Quality Index PM2.5 & PM1.0"
},
axisX:{
title: "chart updates every " + updateInterval / 1000 + " secs"
},
axisY:{
suffix: " μg/m3"
},
data: [
{
type: "line",
yValueFormatString: "##.##",
xValueFormatString: "hh:mm:ss TT",
showInLegend: true,
legendText: "{name} " + yValueP2m5 + " μg/m3",
toolTipContent: "{y} μg/m3",
dataPoints: $dataPointsP2m5
},
{
type: "line",
yValueFormatString: "##.##",
xValueFormatString: "hh:mm:ss TT",
showInLegend: true,
legendText: "{name} " + yValueP1m0 + " μg/m3",
toolTipContent: "{y} μg/m3",
dataPoints: $dataPointsP1m0
}]
});
chartPm.render();
document.getElementById("getMinMaxdataPoints").addEventListener("click",function(){
var maxYp2m5 = -Infinity;
var minYp2m5 = Infinity;
var viewportMinimum = chartPm.axisX[0].get("viewportMinimum");
var viewportMaximum = chartPm.axisX[0].get("viewportMaximum");
for(var i = 0; i < chartPm.data[0].dataPoints.length; i++){
if(chartPm.data[0].dataPoints[i].x >= viewportMinimum && chartPm.data[0].dataPoints[i].x <= viewportMaximum && chartPm.data[0].dataPoints[i].y > maxYp2m5){
maxYp2m5 = charPm.data[0].dataPoints[i].y;
}
if(chartPm.data[0].dataPoints[i].x >= viewportMinimum && chartPm.data[0].dataPoints[i].x <= viewportMaximum && chartPm.data[0].dataPoints[i].y < minYp2m5){
minYp2m5 = chartPm.data[0].dataPoints[i].y;
}
}
document.getElementById("MinimumP2m5Value").innerHTML = " min. "+ minYp2m5;
document.getElementById("MaximumP2m5Value").innerHTML = " max. "+ maxYp2m5;
});
and my html part is:
<body>
<div class="flex-container">
<div>
<button id="getMinMaxdataPoints">Get min/max.</button><br/>
</div>
<div>
<span>PM2.5 & PM1.0</span><br/>
<span id="MinimumP2m5Value"></span><br/>
<span id="MaximumP2m5Value"></span><br/>
</div>
</div>
</body>
In the above code, you have just looped through the dataPoints of first dataSeries. Whereas, you need to first loop through each dataSeries then through the respective dataPoints.
Here is a JSFiddle example for the same.
document.getElementById("getMinMaxdataPoints").addEventListener("click",function(){
var minContent = "";
var maxContent = "";
var viewportMinimum = chart.axisX[0].get("viewportMinimum");
var viewportMaximum = chart.axisX[0].get("viewportMaximum");
for( i = 0; i < chart.data.length; i++){
var maxYp2m5 = -Infinity;
var minYp2m5 = Infinity;
for(var j = 0; j < chart.data[i].dataPoints.length; j++){
if(chart.data[i].dataPoints[j].x >= viewportMinimum && chart.data[i].dataPoints[j].x <= viewportMaximum && chart.data[i].dataPoints[j].y > maxYp2m5){
maxYp2m5 = chart.data[i].dataPoints[j].y;
}
if(chart.data[i].dataPoints[j].x >= viewportMinimum && chart.data[i].dataPoints[j].x <= viewportMaximum && chart.data[i].dataPoints[j].y < minYp2m5){
minYp2m5 = chart.data[i].dataPoints[j].y;
}
}
minContent += "Series " + (i + 1) + " min. Y value " + minYp2m5 + " | ";
maxContent += "Series " + (i + 1) + " max. Y value " + maxYp2m5 + " | ";
}
document.getElementById("MinimumYValue").innerHTML = minContent;
document.getElementById("MaximumYValue").innerHTML = maxContent;
});
The script doesn't throw any errors, but rarely completely works - i.e. complete successfully with all of the expected data in the destination tab. The results breakdown is generally:
no results in the destination sheet - this happens ~50-75% of the time
all of the results in the destination sheet, except in cell A1 - ~25% of the time
100% completely works - ~15-25% of the time
code snippet of the batchupdate() call
var data = [
{
range: (ss.getSheetName() + "!A1:AQ" + valueArray.length)
,values: valueArray
}
];
const resource = {
valueInputOption: "RAW"
,data: data
};
Logger.log("request = " + JSON.stringify(resource)
+ "\n" + "valueArray = " + valueArray.length
);
Logger.log(" Sheets.Spreadsheets.Values.batchUpdate(params, batchUpdateValuesRequestBody) ");
var response = Sheets.Spreadsheets.Values.batchUpdate(resource, spreadsheetId);
Logger.log("response = " + response.toString());
and the response
response = {
"totalUpdatedRows": 37776,
"responses": [{
"updatedCells": 1482389,
"updatedRange": "BatchUpdateDestination!A1:AP37776",
"updatedColumns": 42,
"spreadsheetId": "adahsdassadasdsadaasdasdasdasdasdasdasdasdas",
"updatedRows": 37776
}
],
"spreadsheetId": "adahsdassadasdsadaasdasdasdasdasdasdasdasdas",
"totalUpdatedCells": 1482389,
"totalUpdatedSheets": 1,
"totalUpdatedColumns": 42
}
Its obviously a very large dataset, but I've pruned the destination spreadsheet to ensure there is ample room for the data, and from earlier testing, I believe that a specific size error would be returned if that was the blocker.
How can I troubleshoot, or better yet, prevent these incomplete executions? is there any way to inspect the batch jobs that these requests initiate?
Answering my own question...
After toiling with this a little more, I couldn't figure out any way to troublshooting or inspect the odd, seemingly successfully batchUpdate() jobs. Thus, I resorted to batching the batchUpdate() calls into batches of 15000. This seems to work consistently, though maybe a bit slower:
// This is the very large 2D array that is populated elsewhere
var valueArray = [];
var maxRows = valueArray.length;
var maxCols = valueArray[0].length;
var batchSize = 15000;
var lastBatchSize = 1;
for (var currentRowCount = 1; currentRowCount <= maxRows; ++currentRowCount) {
if( currentRowCount % batchSize == 0
|| currentRowCount == maxRows
)
{
Logger.log("get new valuesToSet");
valuesToSet = valueArray.slice(lastBatchSize - 1, currentRowCount -1);
var data = [
{
range: (ss.getSheetName() + "!A" + lastBatchSize + ":AQ" + (lastBatchSize + valuesToSet.length))
,values: valuesToSet
}
];
const resource = {
valueInputOption: "RAW"
,data: data
};
Logger.log("request = " + JSON.stringify(resource).slice(1, 100)
+ "\n" + "valuesToSet.length = " + valuesToSet.length
);
try {
var checkValues = null;
var continueToNextBatch = false;
for (var i = 1; i <= 3; ++i) {
Logger.log("try # = " + i
+ "\n" + " continueToNextBatch = " + continueToNextBatch
+ "\n" + " make the batchUpdate() request, then sleep for 5 seconds, then check if there are values in the target range."
+ "\n" + " if no values, then wait 5 seconds, check again."
+ "\n" + " if still not values after 3 tries, then resubmit the batchUpdate() requestion and recheck values"
+ "\n" + "range to check = " + "A" + lastBatchSize + ":AQ" + lastBatchSize
);
Logger.log(" Sheets.Spreadsheets.Values.batchUpdate(params, batchUpdateValuesRequestBody) ");
var response = Sheets.Spreadsheets.Values.batchUpdate(resource, spreadsheetId);
Logger.log("response = " + response.toString());
/// loop and check for data in newly written range
for (var checks = 1; checks <= 3; ++checks) {
Utilities.sleep(5000);
var checkValues = ss.getRange(("A" + lastBatchSize + ":AQ" + lastBatchSize)).getValues();
Logger.log("new cell populated - checks # = " + checks
+ "\n" + "range to check = " + "A" + lastBatchSize + ":AQ" + lastBatchSize
+ "\n" + "checkValues.length = " + checkValues.length
+ "\n" + "checkValues = " + checkValues
);
if(checkValues.length > 1)
{
Logger.log("checkValues.length > 1, so continue to next batch"
+ "\n" + "range to check = " + "A" + lastBatchSize + ":AQ" + lastBatchSize
+ "\n" + "checkValues.length = " + checkValues.length
+ "\n" + "checkValues = " + checkValues
);
continueToNextBatch = true;
continue;
}
else
{
Logger.log("checkValues.length is still not > 1, so try the request again"
+ "\n" + "range to check = " + "A" + lastBatchSize + ":AQ" + lastBatchSize
);
}
}
if(continueToNextBatch)
{
continue;
}
}
}
catch (e) {
console.error("range.setValues(valuesToSet) - yielded an error: " + e
+ "\n" + "valuesToSet = " + valuesToSet.length
+ "\n" + "maxRows = " + maxRows
+ "\n" + "maxCols = " + maxCols
+ "\n" + "currentRowCount = " + currentRowCount
+ "\n" + "current range row start (lastBatchSize) = " + lastBatchSize
+ "\n" + "current range row end (j - lastBatchSize) = " + (currentRowCount - lastBatchSize)
);
}
lastBatchSize = currentRowCount;
}
}
I need help! I am making a button that will deal out five random cards, however there cannot be any repeating cards. Right now with the code I have below I get the five random cards but there are repeated cards sometimes. How can I edit the code to make there be no repeats? Thank you!
enter code here
"use strict";
var rank = {
"1": "Ace",
"2": "Deuce",
"3": "Tray",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
"10": "10",
"11": "Jack",
"12": "Queen",
"13": "King",
};
function getRank(i) {
return rank[i]
};
var suits = {
"1": "Hearts",
"2": "Spades",
"3": "Diamonds",
"4": "Clubs",
};
function getSuit(i) {
return suits[i]
};
var main = function() {
console.log(this.id);
if (this.id == "btn3") {
var random1 = Math.floor(Math.random() * 13) + 1;
var random2 = Math.floor(Math.random() * 4) + 1;
var random3 = Math.floor(Math.random() * 13) + 1;
var random4 = Math.floor(Math.random() * 4) + 1
var random5 = Math.floor(Math.random() * 13) + 1;
var random6 = Math.floor(Math.random() * 4) + 1;
var random7 = Math.floor(Math.random() * 13) + 1;
var random8 = Math.floor(Math.random() * 4) + 1
var random9 = Math.floor(Math.random() * 13) + 1
var random10 = Math.floor(Math.random() * 4) + 1
var output = getRank(random1) + " of " + getSuit(random2) + ", " + getRank(random3) + " of " + getSuit(random4) + ", " + getRank(random5) + " of " + getSuit(random6) + ", " + getRank(random7) + " of " + getSuit(random8) + ", " + getRank(random9) + " of " + getSuit(random10);
document.querySelector("div").innerHTML = output;
var buttons = document.querySelectorAll("button");
for (var i = 0; i < buttons.length; ++i) {
buttons[i].addEventListener("click", main);
}
});
Create an array for the entire deck of cards instead of 2 arrays for the rank and suit. You could create this by making a third variable and looping through the suits for each rank and pushing it to the new deck variable. Then you generate random numbers from 1-52 and save each one, checking it against the last ones for a duplicate which you would discard.
OR keep what you have and save each card combination (suit and rank random numbers) to test against.
Either way you need to test the second, third, etc. card against the ones already selected. In the case above, check the combination of random3 and random4 against the combination of random1 and random2. If both match, generate new values for random3 and random4.
I've been working with highcharts and MVC 3 for two years by now (I never done anything complicated, just load data and make it work stuff), and I worked with two different scenarios:
Chart code written in the directly in the view, loading data through Json
Html helper responsible to plot the chart
The Html helper approach seems to me a more elegant choice ... but then, just to illustrate to you guys, here is how it looks like (just part of it):
public static string DisplayChart(
this HtmlHelper helper,
ChartOptions options,
TimedChartSeries[] data)
{
string[] axisList = data.GroupBy(t => t.Unit).Select(t => t.Key).ToArray();
string result = "";
result += "<script type=\"text/javascript\">\n";
result += "var " + options.ChartName + ";\n";
result += "$(document).ready(function() {\n";
result += options.ChartName + "= new Highcharts.Chart({\n";
result += "chart: {renderTo: '" + options.DivName + "',zoomType: ''},\n";
result += "title: { text: '" + options.Title + "'},\n";
result += "subtitle: {text: '" + options.SubTitle + "'},\n";
result += "xAxis: { type: 'datetime'," +
"\n dateTimeLabelFormats: {month: '%e. %b', year: '%b' },"
+ "labels:{rotation: -45}\n},\n";
string axes = "";
for (int i = 0; i < axisList.Length; i++)
{
var temporaryData = data.First(t => t.Unit == axisList[i]);
if (i != 0)
axes += ", ";
axes += "{labels:{\n " +
"formatter : function(){return this.value + '" + temporaryData.Unit + "';},\n" +
"style:{color:'#" + temporaryData.Color.Name.Remove(0, 2) + "'}},\n" +
"title:{text:'',style:{color:'#" + temporaryData.Color.Name.Remove(0, 2) + "'}},\n" +
"}\n";
}
result += "yAxis: [\n" + axes + "],\n";
string units = "";
for (int i = 0; i < data.Length; i++)
{
if (i != 0)
units += ", ";
units += "'" + data[i].Title + "': '" + data[i].Unit + "'\n";
}
result += "tooltip:{\nshared: true,\n backgroundColor: 'none' ,\nborderColor: 'none'," +
"\nshadow:false\n ,crosshairs: true,\n" +
"formatter: function() {var s = '<table class=\"table-list\"><tr><th>Hora</th><th>'+ test(this.x) +'</th></tr>';" +
"\n$.each(this.points, function(i, point) {" +
"\ns += '<tr><td>'+point.series.name + '</td><td>'+point.y+'</td></tr>'});s+='</table>';" +
"\n$('#tooltip').html(s);}},";
result += "lang: {" +
"months: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho'," +
"'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro']," +
"weekdays: ['Domingo', 'Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta', 'Sábado']},";
string series = "";
int x = 0;
for (int j = 0; j < axisList.Length; j++)
{
var temporaryData = data.Where(t => t.Unit == axisList[j]).ToArray();
for (int i = 0; i < temporaryData.Length; i++)
{
if (x > 0)
series += ", ";
series += "{name: '" + temporaryData[i].Title + "',\n color: '#" + temporaryData[i].Color.Name.Remove(0, 2) +
"', \ntype: '" + temporaryData[i].Type + "',\nunit:'" + temporaryData.First().Unit + "', \nyAxis:" + j + " , \ndata:[" + FromArrayToString(temporaryData[i].Data) +
"], marker: { enabled: false}}\n";
x++;
}
}
result += "series: [\n" + series + "]\n";
result += "});});";
result += "\nfunction test(i)\n{\nvar j = new Date(i + 2*60*60*1000);\n" +
"return new Date(i + 3*60*60*1000).format('d/m/Y H:i:s.')+j.getMilliseconds();\n}\n</script>";
result += "\n<div id=\"" + options.DivName + "\" style=\"width:" + options.Width + ";height: " + options.Height + "\"></div>" +
"<div id=\"tooltip\"></div>";
return result;
}
It's really simple to call this helper:
#Html.Raw(Html.DisplayChart((ChartOptions)Model.Options,(TimedChartSeries[])Model.Series))
As you guys can see, I have to use the Html.Raw helper in order to make it work ... that is problem nº 1 (and it probably has an easy solution). But the second problem is really great: the chart becomes entirely tied to my domain. If I wanted to plot a, say, bar chart displaying data of the last 3 years in months (each month being represented by a bar), it would be impossible to use this helper.
And it also looks kind of ugly.
So, guys, which option do you think is more elegant, the Json or the Helper approach?
About the use of Html.Raw and the easy solution:
Change your function to
public static HtmlString DisplayChart(this HtmlHelper helper, ...)
{
...
return new HtmlString(result);
}
You then may use #Html.DisplayChart(...) in your razor views.
Also, please make sure that options.DivName, options.Title, options.SubTitle etc. are properly escaped - a title like Everybody's favorite chart will break the output.