Node.js / JavaScript - JavaScript window variable breaking functionality - javascript

So on my Jade template, I am passing it a variable through the route which contains an array of names.
Here is the JavaScript from that template:
script(type='text/javascript')
window.teams = !{JSON.stringify(teams)};
teams contains the array I spoke about. Here is the JavaScript:
$(function () {
// Array of team names
var teamNames = [];
for(var i = 0; i < teams.length; i++) {
teamNames.push(teams[i].name);
}
var bracketTeams = [];
var teamMatches = new Array();
for(var i=0;i<teamNames.length; i+=2)
{
teamMatches.push([teamNames[i],teamNames[i+1]]);
}
var bracketData = {
teams : teamMatches,
results : [[
[ [1, 0], [1, 0] ],
[ [1, 0], [1, 0] ]
]]
}
$('#tournamentBrackets').bracket({
init: bracketData
});
});
Now for some reason, the functionality of the rest of the system completely breaks when I add in this JavaScript code. If I comment it out, then the rest of the system works fine, if I leave it in, buttons do nothing, links go nowhere, data isn't added (although the data is loaded, i.e a database of teams is listed). If I load the page, then uncomment the JS, the page works as it should though.
Any ideas what is breaking my system in this code? I just tried renaming window.teams to window.testName and it still broke. Really confused.
EDIT: HTML generated
http://pastebin.com/VdkEfANb

In Node.js, the window object probably does not exist. Node.js has a different global object than browsers do. In Node.js, the global object is actually named global. Unless you have specifically set up an object named window, you're probably getting a ReferenceError when you try to assign values to window.teams

Related

Javascript - List (array) length and getting an item from the list not working

I'm practicing JavaScript (just started this week) on vscode with the Quokka.js extension. Right now I'm initializing a list called "things" and trying to get the length of the list and getting some items out of it. This is my code:
var things = ['bananas', 'apples', 7];
things.length;
things[0];
The last two rows return nothing to me, not even "undefined".
How do I get vscode to return the length and the first object from the list using [0]? If it is not possible in vscode, what program should I use for learning JavaScript?
I also tried initializing the list as an array with
Array things = ['bananas', 'apples', 7];
but this does not seem to be allowed.
Moreover, for example the command
things.splice
seems to work in vscode.
Even if you're using Quokka, it's better to output using console.log. Quokka works very well with console.log.
Also try not to use var or declare array using Array. This is JavaScript, not Java.
// Do not use var
let things = ['bananas', 'apples', 7];
console.log(things.length);
console.log(things[0]);
// This will not work
// This does not make any sense either
Array things = ['bananas', 'apples', 7];
JavaScript Array is not Class or an interface by using which you can declare it's instances. JavaScript Array is a global object. There are no classes in JavaScript.

JS arrays 3 deep getting lost

Learing JS by game. and of course the simplist is a clicker :-)
OK, so I have had lots of help with this, but the array section is just not sinking in. JS arrays do not work like arrays I am used to. two part question:
1. Why does the following code keep saying weaponlevelIfo[0] undefined, when it is defined? Please explain your answer, don't just correct mine LOL
2. I am more interesting in populating the code at runtime
As stated, all the research I am coming across as well as videos, talk about static data, i.e. it is put in at programing level, not run time.
I have had a really patient community person that has tried to help me understand JS arrays, but I must be blind as I am not seeing it. I can do in in other language just fine. but JS? nope.
// produces weaponLevelNfo[weaponId][level][cost] and [goldperclick]
// weapon, level 1-9, cost/goldperclick on each level
var weaponLevelNfo = new Array(14); // Outter array comprised of weapons 0-14
function initGame() {
for (let i=0; i <= weaponLevelNfo.length; i++) {
weaponLevelNfo[i] = new Array(9); // create leves array under weaponid array
for (let j = 0; j < weaponLevelNfo[i].length; j++) {
// loop through each 9 levels changing as needed
weaponLevelNfo[i][j] = new Array(2); // create an object for readability
}
}
}
initGame();// added - forgot to add this in the original post (sorry)
weaponLevelNfo[0][0][2]=3;
console.log(weaponLevelNfo[0][0][2]);
// always gives me weaponLevelNfo[0] not defined
I prefer the results to be as such
weaponLevelNfo[x][y].cost,
weaponLevelNfo[x][y].incomePerClick,
but am quite happy with
weaponLevelNfo[x][y][z],
weaponLevelNfo[x][y][z],
But as you can see from the code, assigning them direct or at runtime, I get the not defined error
What is missing to allow me to assign these at run time?
Two issues:
You need to call initGame to create all those subarrays, otherwise weaponLevelNfo[0] is not defined and so weaponLevelNfo[0][0] will trigger the error you get.
Your outer loop performs one iteration too many (<=). Change:
for (let i=0; i <= weaponLevelNfo.length; i++) {
by
for (let i=0; i < weaponLevelNfo.length; i++) {
Without that change, the last iteration is actually adding an element to the array in slot i, and so the length of the array increases... the loop becomes infinit.
Note that there are shorter ways to define such a nested array. For instance:
var weaponLevelNfo = Array.from({length:14}, () => Array.from({length:9}, () => [0, 0]));
When you create an array in javascript with new Array(2) this is an array with two positions. In this example (new Array(2)), you can access it at index 0 and index 1. Consider the following:
var newArr = new Array(2);
You can then access the two positions by the following:
var position1 = newArr[0];
var position2 = newArr[1];
So when you try this:
var position = newArr[2];
This will throw an undefined exception.
You can change the end of your example code to this:
weaponLevelNfo[0][0][1]=3;
console.log(weaponLevelNfo[0][0][1]);
You define array with two elements Array(2) with indexes 0 and 1 but you use index 2.
To initialize multidimensional array filled by zeros (if not remove .fill(0)) use
[...Array(14)].map(x=>[...Array(9)].map(y=>Array(2).fill(0)));
let a=[...Array(14)].map(x=>[...Array(9)].map(y=>Array(2).fill(0)));
a[13][8][1]=3;
console.log('Last element value:',a[13][8][1]);
console.log(JSON.stringify(a));

Difference between initializing array length and assign to index vs repeatedly pushing

While working on building a list of sheet names, I came across this question:
List names of sheets in Google Sheets and skip the first two
Saving you the click, this person's solution is: (Stripped down, pseudo code)
getSheets() // Get all the sheets in spreadsheet workbook
var out = new Array( sheets.length+1 ) ;
for (var i = 1 ; i < sheets.length+1 ; i++ )
out[i] = [sheets[i-1].getName()];
return out
My solution would have leveraged:
...
var sheetName = sheet[i].getName();
out.push(sheetName);
The first solution seems to dynamically create empty array values, then later declare their value. While I have always just pushed new values into the array.
What is the difference?
In which situations is one better than the other?
In which situations should either be avoided?
Your code and the original code do quite different things.
Assuming sheets has objects in it that return the names "sheet1", "sheet2", and "sheet3" from getName, the original code creates an array that looks like this:
[
(missing),
["sheet1"],
["sheet2"],
["sheet3"]
]
Note two things:
There is no entry at index 0. (It literally doesn't exist at all, which is subtly different from existing and containing the value undefined.)
The other entries are single-element arrays, each containing its sheet name.
Your code creates this instead:
[
"sheet1",
"sheet2",
"sheet3"
]
Presumably the author had a reason for skipping index 0 and creating subordinate arrays (I'd guess because they were passing that result array into some API function that expects something in that form).
So there's no really a "better" or "worse" here, just different.
If your fundamental question is whether this:
var original = ["one", "two", "three"];
var updated = [];
for (var i = 0; i < original.length; ++i) {
updated[i] = original[i].toUpperCase(); // Or whatever
}
is better/worse than this:
var original = ["one", "two", "three"];
var updated = [];
for (var i = 0; i < original.length; ++i) {
updated.push(original[i].toUpperCase()); // Or whatever
}
the answer is: It's really a matter of style. Performance isn't markedly different between the two (and rarely matters), and amusingly one way is faster on some JavaScript engines and the other way is faster on others.
Both of those can probably be better expressed using map:
var original = ["one", "two", "three"];
var updated = original.map(function(entry) { return entry.toUpperCase(); });
I think Google Sheets has map, even though it mostly has only ES3-level features otherwise.
Side note: new Array is almost never the right way to create an array.

Ways to interpolate template variables inside JavaScript objects

I'd like to do the following
var obj = {
animal: "${animal}"
};
var res = magic(obj, {animal: "cat"});
// res => {animal: "cat"}
magic is some function that does the dirty work. Obviously obj could be more complex with multiple keys, nested arrays and so on. The template variable could be inside an array like this
var obj = {
animals: ["cat", "dog", "${animal}", "cow"]
};
and it could be anywhere in the array so simply doing obj.animals[2] = "bat"; isn't feasible.
I've found the underscore-tpl library with which I can achieve what I want, but I would like to know if there are other solutions for future reference and because I had a hard time finding underscore-tpl in the first place.
My actual use is case is that I have a config.json file where I have several declarations like the following
{
"task1": {
"command": "command-line-program",
"args": [
"--input", "{{input}}",
"--flag1",
"--output", "{{output}}",
"--flag2",
],
"options": {
"cwd": "path-to-working-dir"
}
}
}
I parse this consig.json using JSON.parse(...) and I call require("child_process").spawn with the command, args and options parameters declared in the file, however args change a lot, flags added, reordered and stuff, so simply doing config.task1.args[1] = "<input value>"; involves changing the code that invokes spawn and this is as error prone as it gets.
Update
Based on the accepted answer I've created a simple package (located here) which I can include in my projects, feel free to use it.
You could JSON.stringify the object, then replace your search value with the actual value, then JSON.parse the result:
function magic(o, a) {
var j = JSON.stringify(o);
for (var k in a) {
j = j.split('${'+k+'}').join(a[k]);
}
return JSON.parse(j);
}
I suggest to you a very simple but very fast and understandable template engine:
simple-template.js
It consists of 22 lines of code, very simple!
Considering that, you will be able to render your config easily!

How do I match this text faster?

I'm building an autosuggest for names. When the user types in the textbox, it hits the server and runs this:
var names = [ list of 1000 names ]; //I have a list of 1000 names, this is static.
var query = 'alex';
var matched_names = [];
//This is when it gets slow....
names.forEach(function(name){
if(name.indexOf(query) >= 0){
matched_names.push(name);
}
});
return matched_names;
How can I make this faster? I'm using Node.js
If the names are static then move this code to the client and run it there. The only reason to run code like this on the server is if the data source is dynamic in some way.
Doing this logic client-side will dramatically improve performance.
You should probably use filter instead, for one thing, because it's native:
var names = [ /* list of 1000 names */ ];
var query = 'alex';
var matched_names = names.filter(function(name) {
return name.indexOf(query) > -1;
});
return matched_names;
If you store the names in sorted order, then you can use binary search to find the region of names within the sorted order that start with the fragment of name that the user has typed so far, instead of checking all the names one by one.
On a system with a rather odd programming language, where I wanted to find all matches containing what the user had typed so far in any position, I got a satisfactory result for not much implementation effort by reviving http://en.wikipedia.org/wiki/Key_Word_in_Context. (Once at university I searched through a physical KWIC index, printed out from an IBM lineprinter, and then bound as a document for just this purpose.
I would suggest you to do this stuff on the client-side and prefer (for now) a while loop instead of a filter/forEach approach:
var names = [ /* list of 1000 names */ ]
, query = 'alex'
, i = names.length
, matched_names = [];
while(i--){
if(names[i].indexOf(query) > -1){
matched_names.push(names[i]);
}
}
return matched_names;
This will be much faster (even if filter/forEach are natively supported). See this benchmark: http://jsperf.com/function-loops/4

Categories

Resources