I inherited a codebase that has this style of code and no tests:
var series1 = [];
var series2 = [];
var series3 = [];
var series4 = [];
var series5 = [];
var series6 = [];
var series7 = [];
var series8 = [];
for (var y = 1; y <= seriesData.length; y++) {
// columns are series
eval("series" + y).push({
label: "series" + y,
lineColor: colorArr[seriesData[y - 1].colorIndex],
x: sampleTime,
y: rows[x][seriesData[y - 1].index]
});
}
The main problem is that we're going to start accommodating more than 8 sets of data. Personally, I don't really appreciate this style of code, and I've read the eval function can be harmful in JS. Is there a better way to refactor this?
What I've tried:
let multiarr = []
for (var y = 1; y <= seriesData.length; y++) {
// columns are series
let arr = [];
arr.push({
label: "series" + y,
lineColor: colorArr[seriesData[y - 1].colorIndex],
x: sampleTime,
y: rows[x][seriesData[y - 1].index]
});
}
multiarr.push(arr);
The initial code seems a bit strange. You have a block of series arrays, but each one only gets pushed with a single item. Couldn't it simply be reduced to:
const result = seriesData.map((item, i) => ({
label: `series${i + i}`,
lineColor: colorArr[item.colorIndex],
x: sampleTime,
y: rows[x][item.index]
}))
If for some reason, you do actually need each of the items to themselves be an array, just do:
const multiarr = seriesData.map((item, i) => [{
label: `series${i + i}`,
lineColor: colorArr[item.colorIndex],
x: sampleTime,
y: rows[x][item.index]
}])
You could collect all arrays in a single array and push by taking an index.
var series1 = [],
series2 = [],
series3 = [],
series4 = [],
series5 = [],
series6 = [],
series7 = [],
series8 = [],
data = [series1, series2, series3, series4, series5, series6, series7, series8];
for (var y = 0; y < seriesData.length; y++) {
data[y].push({
label: "series" + (y + 1),
lineColor: colorArr[seriesData[y].colorIndex],
x: sampleTime,
y: rows[x][seriesData[y].index]
});
}
Related
I'm using this object detection model. And I'd like to get bounding box, class labe and score. I think this model returns bounding box and so on. however it's returns diferent results.
My code
function getMethods(o) {
return Object.getOwnPropertyNames(Object.getPrototypeOf(o))
.filter(m => 'function' === typeof o[m]);
}
const model = await tf.loadGraphModel("https://tfhub.dev/tensorflow/tfjs-model/ssd_mobilenet_v2/1/default/1", {fromTFHub: true});
console.log("Get methods");
console.log(getMethods(model));
const cat = document.getElementById('cat');
let tensor = tf.browser.fromPixels(cat);
tensor = tf.cast(tensor, 'int32');
let y_pred = await model.executeAsync({'image_tensor': tensor.expandDims(0)});
console.log('Predictions: ');
console.log(y_pred);
Results
(12) ["constructor", "findIOHandler", "load", "loadSync", "save", "predict", "normalizeInputs", "normalizeOutputs", "execute", "executeAsync", "convertTensorMapToTensorsMap", "dispose"]
Predictions
[
{
"kept": false,
"isDisposedInternal": false,
"shape": [
1,
1917,
90
],
"dtype": "float32",
"size": 172530,
"strides": [
172530,
90
],
"dataId": {
"id": 1959
},
"id": 1188,
"rankType": "3",
"scopeId": 1382
},
{
"kept": false,
"isDisposedInternal": false,
"shape": [
1,
1917,
1,
4
],
"dtype": "float32",
"size": 7668,
"strides": [
7668,
4,
4
],
"dataId": {
"id": 2019
},
"id": 1239,
"rankType": "4",
"scopeId": 1475
}
]
If I get bounding box, class labe and score, How should I change my code?
each model is different, this one is on tfhub model page: https://tfhub.dev/tensorflow/ssd_mobilenet_v2/2 - you can see what each tensor in output result is
anyhow, model returns all possible scores, classes and boxes without any filtering - which means you need to post-process those results, best using tf.image.nonMaxSuppressionAsync() so you can filter by minimum score and get only boxes you want back
then match box indexes with index of class tensor output to match box with class
you can look up reference implementation at https://github.com/tensorflow/tfjs-models/blob/master/coco-ssd/src/index.ts, key function is infer
There is a ready made code proovided by Paul Van Eck (IBM Developer) channel from YouTube (code snippet works for me, but as a model not good):
const scores = y_pred[0].dataSync();
const numBoxesFound = y_pred[0].shape[1];
const numClassesFound = y_pred[0].shape[2];
const maxScores = [];
const classes = [];
// for each bounding box returned
for (let i = 0; i < numBoxesFound; i++) {
let maxScore = -1;
let classIndex = -1;
// find the class with the highest score
for (let j = 0; j < numClassesFound; j++) {
if (scores[i * numClassesFound + j] > maxScore) {
maxScore = scores[i * numClassesFound + j];
classIndex = j;
}
}
maxScores[i] = maxScore;
classes[i] = classIndex;
}
const boxes = tf.tensor2d(y_pred[1].dataSync(), [y_pred[1].shape[1], y_pred[1].shape[3]]);
const indexTensor = tf.image.nonMaxSuppression(boxes, maxScores, maxNumBoxes, 0.5, 0.5);
const indexes = indexTensor.dataSync();
const count = indexes.length;
const objects = [];
for (let i = 0; i < count; i++) {
const bbox = [];
for (let j = 0; j < 4; j++) {
bbox[j] = rrr[1].dataSync()[indexes[i] * 4 + j];
}
const minY = bbox[0] * image_height; // <= width & height you can get it by gm nodejs module
const minX = bbox[1] * image_width;
const maxY = bbox[2] * image_height;
const maxX = bbox[3] * image_width;
objects.push({
bbox: [minX, minY, maxX, maxY],
label: labels[classes[indexes[i]]],
score: maxScores[indexes[i]]
});
}
console.log(objects);
// output:
[
{
bbox: [
25.680667877197266,
991.999267578125,
2295.833375930786,
3062.655055999756
],
label: 'background',
score: 0.900220513343811
}
]
I created an array using the array class with size of 8 * 8 and filling it with a dumy object using fill . and after that I created a for and assigning the values . but when I print the array . the same value is there for all the field .
var gridSize = 8;
const colorname = ["Red", "Orange", "Green", "Blue", "Purple", "Yellow"];
var gameGrid = new Array(gridSize * gridSize).fill({
loc: null,
color: null,
index: null,
value: null
});
fillTheGrid();
function fillTheGrid() {
for (var i = 0; i < gridSize * gridSize; i++) {
addElement(i)
}
}
// actual code Is big so im using I for index and value
function addElement(i){
gameGrid[i].loc = i;
let randomColor = Math.floor(Math.random() * colorname.length);
gameGrid[i].color = colorname[randomColor];
gameGrid[i].index = i;
gameGrid[i].value = i;
}
log(gameGrid);
expected output is
[{"loc":1,"color":"Green","index":1,"value":1},{"loc":2,"color":"Red","index":2,"value":2},......,{"loc":63,"color":"Green","index":63,"value":63}]
but im getting
[{"loc":63,"color":"Green","index":63,"value":63},...,{"loc":63,"color":"Green","index":63,"value":63}]
that is im getting same value for all
In JS objects are passed by reference, so when you do
var gameGrid = new Array(gridSize * gridSize).fill({
loc: null,
color: null,
index: null,
value: null
});
the same object is put in each spot - if you update any of them, you update "all" of them. What you need to do is put a different object in each spot by doing:
var gameGrid = new Array(gridSize * gridSize).fill(null).map(i => ({
loc: null,
color: null,
index: null,
value: null
});
var gridSize = 8;
const colorname = ["Red", "Orange", "Green", "Blue", "Purple", "Yellow"];
var gameGrid = new Array(gridSize * gridSize).fill(null).map(i => ({
loc: null,
color: null,
index: null,
value: null
}));
fillTheGrid();
function fillTheGrid() {
for (var i = 0; i < gridSize * gridSize; i++) {
addElement(i)
}
}
// actual code Is big so im using I for index and value
function addElement(i){
gameGrid[i].loc = i;
let randomColor = Math.floor(Math.random() * colorname.length);
gameGrid[i].color = colorname[randomColor];
gameGrid[i].index = i;
gameGrid[i].value = i;
}
console.log(gameGrid);
If you look at the description of Array.prototype.fill() method on MDN, it says:
If the first parameter is an object, each slot in the array will
reference that object
So unlike you expected, there is only one object in memory and each slot in the array is referencing that same object.
Following code snippet demonstrates this behavior
let arr = Array(3).fill({});
arr[0].a = "hello";
arr.forEach(obj => console.log(obj));
Solution:
There is no need to initially fill the array with objects, you could just create a new object each time addElement() function, add key-value pairs in the newly created object and add this object in the array.
var gridSize = 8;
const colorname = ['Red', 'Orange', 'Green', 'Blue', 'Purple', 'Yellow'];
var gameGrid = new Array(gridSize * gridSize).fill(null);
fillTheGrid();
function fillTheGrid() {
for (var i = 0; i < gridSize * gridSize; i++) {
addElement(i);
}
}
function addElement(i) {
const obj = {};
obj.loc = i;
let randomColor = Math.floor(Math.random() * colorname.length);
obj.color = colorname[randomColor];
obj.index = i;
obj.value = i;
gameGrid[i] = obj;
}
console.log(gameGrid);
i have imbrecated json objects, i am struggling with this error when i try to loop inside PieData to fill pieChart this is my code
var PieData = [
for(b in quizs[i].quests[j].reps){
//quizs[i].quests[j]["quizId"]
/* if(quizs[i].quests[j].reps[b]["stat"]==null){
var l = 1;
}
else{
var l =quizs[i].quests[j].reps[b]["stat"]
}*/
{
{%set h = 'hex'%}
value : 2,
startAngle: 240,
color : '{{h}}',
label : quizs[i].quests[j].reps[b]["rep"]
}, }
];
when i if i try to change to static for => for (var r = 0; r < 2; r++) {
i always get the same error, any help is appreciatred
You can't use for in array declaration.
var PieData = [];
for (var b in quizs[i].quests[j].reps) {
PieData.push({
value: 2,
startAngle: 240,
color: "{{h}}",
label: quizs[i].quests[j].reps[b]["rep"]
});
}
Array#map will looks cleaner here
var PieData = quizs[i].quests[j].reps.map(i => ({
value: 2,
startAngle: 240,
color: "{{h}}",
label: i.rep
}));
My custom X-axis values are not displayed in flot js.
Code for drawing the line chart as below :
var length = 0;
var length1 = 0;
var dataXY = [];
var data = [];
var dataX = [];
length = allVenues.length;
for (var i = 0; i < length; i++) {
length1 = allVenues[i].length;
for (var j = 0; j < length1; j++) {
dataX.push([j, returnDate(allVenues[i][j].date)]);
dataXY.push([returnTimestamp(allVenues[i][j].date), allVenues[i][j].price, "<b>X</b> : " + returnDate(allVenues[i][j].date) + " | " + " <b>Y</b>: " + allVenues[i][j].price]);
}
}
var result = {'label': 'Demo Graph', 'data': dataXY};
data = [result];
var options = {
lines: {
show: true
},
points: {
show: true
},
xaxis: {
ticks: dataX
},
grid: {
hoverable: true,
clickable: true
},
tooltip: {
show: true,
content: "%s | X: %x | Y: %y"
}
};
function returnTimestamp(val) {
var dateTime = new Date(val);
return moment(dateTime).unix();
}
function returnDate(val) {
var dateTime = new Date(val);
return moment(dateTime).format("YYYY-MM-DD hh:mm:ss A");
}
$.plot("#placeholder", data, options);
dataXY array values are:
{"label":"Demo Graph","data":[[1455776629,12],[1455801889,30],[1455962948,45]]}
dataX array values are:
[[0, "2016-02-18 11:53:49 AM"], [1, "2016-02-18 06:54:49 PM"], [2, "2016-02-20 03:39:08 PM"]]
Now i want to set this "dataX" array as X axis values (ticks) on the chart.
This values display below in each point of line graph with X-Axis.
Here dataX and dataXY from allVenues Json Array.
My graph is woking fine except the X-Axis data. You can see in the image below.
Your dataX array for the ticks must have the same x values (timestamps) as your real data array:
[[1455776629, "2016-02-18 11:53:49 AM"], [1455801889, "2016-02-18 06:54:49 PM"], [1455962948, "2016-02-20 03:39:08 PM"]]
PS: I would also suggest to put a linebreak (<br>) between date and time in the labels.
Ok so there are a lot of "Conway's Game of Life" questions but this one is pretty specific. I'm going to have to throw a bunch of code at you first, break it down and show you where the issue is.
So here is my Conway's Game of Life implementation so far, right now it is limited to the console for debugging (JSfiddle - http://jsfiddle.net/georeith/C9Gyr/8/ - fire it up, open your console):
var utils = {};
/*
* utils.extend()
* - Extend initial object with all properties of following objects, objects later in the argument list take precedence.
*/
utils.extend = function(obj) {
var args = Array.prototype.slice.call(arguments, 1);
for (var i = args.length; i--;) {
for (var prop in args[i]) {
obj[prop] = args[i][prop];
}
}
return obj;
}
/*
* utils.defaults()
* - Overwrite initial object with properties of following objects only if key is present in the initial object.
*/
utils.defaults = function(obj) {
var args = Array.prototype.slice.call(arguments, 1);
for (var i = args.length; i--;) {
for (var prop in args[i]) {
if (obj.hasOwnProperty(prop)) {
obj[prop] = args[i][prop];
}
}
}
return obj;
}
/* no-wrap positioning functions */
var calcPos = {
ul: function(cell) {
return [cell.x - 1, cell.y - 1];
},
um: function(cell) {
return [cell.x, cell.y - 1];
},
ur: function(cell) {
return [cell.x + 1, cell.y - 1];
},
l: function(cell) {
return [cell.x - 1, cell.y];
},
r: function(cell) {
return [cell.x + 1, cell.y];
},
ll: function(cell) {
return [cell.x - 1, cell.y + 1];
},
lm: function(cell) {
return [cell.x, cell.y + 1];
},
lr: function(cell) {
return [cell.x + 1, cell.y + 1];
}
}
var worldDefaults = {
rows: 50,
columns: 50,
wrap: true, // left edge is mirrored on right, top edge is mirrored on bottom. Vice versa
speed: -1, // milliseconds (minimum time, waits until end of last tick to calculate from)
grid: []
}
var World = function (opts) {
this.settings = utils.defaults(worldDefaults, opts);
this.maxX = this.settings.columns - 1;
this.maxY = this.settings.rows -1;
for (var y = 0, yLen = this.settings.rows; y < yLen; ++y) {
for (var x = 0, xLen = this.settings.columns; x < xLen; ++x) {
if (y === 0) {
this.cellList.push([]);
if (this.settings.grid.length <= x) {
this.settings.grid.push([]);
}
}
var cell = new Cell();
cell.x = x;
cell.y = y;
cell.alive = !!this.settings.grid[x][y];
if (cell.alive) {
this.lifeList.push(cell);
}
var lx = (x) ? x - 1 : this.maxX;
var uy = (y) ? y - 1 : this.maxY;
var ux = (x == this.maxX) ? 0 : x + 1;
var ly = (y == this.maxY) ? 0 : y + 1;
cell.neighbourCoords = (this.settings.wrap) ?
[
[lx, uy], [x, uy], [ux, uy],
[lx, y], /*[x, y]*/ [ux, y],
[lx, ly], [x, ly], [ux, ly]
]
:
[
calcPos.ul, calcPos.um, calcPos.ur,
calcPos.l, calcPos.r,
calcPos.ll, calcPos.lm, calcPos.lr
]
;
this.cellList[x][y] = cell;
}
}
}
World.prototype.generation = 0;
World.prototype.cellList = [];
World.prototype.lifeList = [];
World.prototype.changeList = [];
World.prototype.nextTick = null;
/* Progresses the world */
World.prototype.tick = function() {
var newLifeList = [];
this.changeList = [];
// This hash goes out of scope after each tick allowing any dead shadowCells to be garbage collected
if (!this.settings.wrap) {
var shadowCellHash = {};
}
for (var i = 0, iLen = this.lifeList.length; i < iLen; ++i) {
var cell = this.lifeList[i];
if (cell.key) {
shadowCellHash[cell.key] = cell;
}
cell.neighbours = 0;
cell.lastIterated = this.generation;
for (var j = 0, jLen = cell.neighbourCoords.length; j < jLen; ++j) {
var coords;
var neighbour;
if (this.settings.wrap) {
coords = cell.neighbourCoords[j];
neighbour = this.cellList[coords[0]][coords[1]];
} else {
coords = cell.neighbourCoords[j](cell);
if (coords[0] > this.maxX || coords[0] < 0 || coords[1] > this.maxY || coords[1] < 0) {
// This neighbour is off the screen so will require a shadowCell
var key = ''+coords[0]+','+coords[1];
if (!shadowCellHash[key]) {
neighbour = shadowCellHash[key] = new ShadowCell(coords[0], coords[1]);
neighbour.neighbourCoords = cell.neighbourCoords;
} else {
neighbour = shadowCellHash[key];
}
} else {
neighbour = this.cellList[coords[0]][coords[1]];
}
}
if (neighbour.lastIterated !== this.generation) {
neighbour.neighbours = 0;
neighbour.lastIterated = this.generation;
}
if (neighbour.alive !== neighbour.changed) {
// neighbour started as alive
++cell.neighbours;
} else {
// neighbour started as dead
++neighbour.neighbours;
if (neighbour.neighbours === 3) {
neighbour.alive = true;
neighbour.changed = true;
neighbour.changeIndex = this.changeList.push(neighbour) - 1;
} else if (neighbour.neighbours === 4) {
// neighbour has reverted to dead
neighbour.alive = false;
neighbour.changed = false;
neighbour.changeIndex = -1;
this.changeList[neighbour.changeIndex] = undefined;
}
}
}
if (cell.neighbours < 2 || cell.neighbours > 3) {
cell.changed = true;
cell.alive = false;
cell.changeIndex = this.changeList.push(cell) - 1;
} else {
newLifeList.push(cell);
}
}
for (var i = 0, iLen = this.changeList.length; i < iLen; ++i) {
var cell = this.changeList[i];
if (cell !== undefined) {
cell.changeIndex = -1;
if (cell.alive) {
newLifeList.push(cell);
}
cell.update();
cell.changed = false;
}
}
this.lifeList = newLifeList;
++this.generation;
this.onTick();
var that = this;
if (this.settings.speed >= 0) {
this.nextTick = setTimeout(function() {
that.tick();
}, this.settings.speed);
}
return this;
}
World.prototype.out = function() {
var s = '';
for (var y = 0, yLen = this.settings.rows; y < yLen; ++y) {
for (var x = 0, xLen = this.settings.columns; x < xLen; ++x) {
s += (this.cellList[x][y].alive)? '\u2B1B' : '\u2B1C';
}
s += '\n';
}
s += '\u21B3 Generation: ' + this.generation + ' -- Cells: ' + this.lifeList.length + ' \u21B5';
s += '\n';
return s;
}
World.prototype.stop = function() {
this.speed = -1;
}
World.prototype.onTick = function() {
return this;
}
var Cell = function() {
return this;
}
Cell.prototype.x = 0;
Cell.prototype.y = 0;
Cell.prototype.neighbours = 0;
Cell.prototype.alive = false;
Cell.prototype.changed = false;
Cell.prototype.changeIndex = -1;
Cell.prototype.lastIterated = -1;
/*
* ShadowCell
* - non rendered cell for use in no-wrap
*/
var ShadowCell = function(x,y) {
this.x = x;
this.y = y;
this.key = ''+this.x+','+this.y;
return this;
}
ShadowCell.prototype = utils.extend({}, Cell.prototype);
ShadowCell.prototype.isShadow = true;
ShadowCell.prototype.update = function(){
return this;
};
/*
* Cell.update()
* - Update cell after tick
*/
Cell.prototype.update = function() {
this.render();
return this;
}
/*
* Cell.render()
* - Placeholder function to be overwritten by rendering engine
*/
Cell.prototype.render = function() {
return this;
}
The method I have chosen involves an array of all the cells that are alive at the start of each generation. I then iterate over each of their 8 neighbours and decide whether to create/delete them.
This works great when I pass wrap: false to the World constructor (see JSfiddle for implementation), this tells it to mirror the sides and not allow overflow. However that style of layout breaks a lot of patterns as it causes cells to come back on themselves so I also want to allow it to calculate beyond the grid.
For this purpose I created the ShadowCell class which behaves mostly the same as the Cell class (each grid cell dead or alive is an instance of it) except that the ShadowClass is only created when a non-existent cell is required outside of the grid and is offered for garbage collection the moment it is no longer required (if it is dead after each generation). Otherwise it mimics the Cell classes attributes and fits directly into the same logic that Cell does.
The issue
If you go to "generation 4" in the console output you may notice it isn't quite right...
I have narrowed this issue down to the ShadowCell implementation because this works if I provide enough padding around the shape so that it does not overflow the grid (which is when ShadowCell kicks in), although like I said earlier ShadowCell is a copy of the Cell class, it has the same attributes and gets passed in as if it was a Cell.
Because I want these to be garbage collected I do not include these in the overall grid array World.cellList... this leads me to believe the problem lies in this section of code:
// This hash goes out of scope after each tick allowing any dead shadowCells to be garbage collected
if (!this.settings.wrap) {
var shadowCellHash = {};
}
for (var i = 0, iLen = this.lifeList.length; i < iLen; ++i) {
var cell = this.lifeList[i];
if (cell.key) {
shadowCellHash[cell.key] = cell;
}
cell.neighbours = 0;
cell.lastIterated = this.generation;
for (var j = 0, jLen = cell.neighbourCoords.length; j < jLen; ++j) {
var coords;
var neighbour;
if (this.settings.wrap) {
coords = cell.neighbourCoords[j];
neighbour = this.cellList[coords[0]][coords[1]];
} else {
coords = cell.neighbourCoords[j](cell);
if (coords[0] > this.maxX || coords[0] < 0 || coords[1] > this.maxY || coords[1] < 0) {
// This neighbour is off the screen so will require a shadowCell
var key = ''+coords[0]+','+coords[1];
if (!shadowCellHash[key]) {
// ShadowCell not in hash, let's create one
neighbour = shadowCellHash[key] = new ShadowCell(coords[0], coords[1]);
neighbour.neighbourCoords = cell.neighbourCoords;
// NOTE: neighbourCoords are a set of functions that return values relative to the cell you pass to them. I am not literally giving the `ShadowCell` the same neighbour positions here.
} else {
neighbour = shadowCellHash[key];
}
} else {
// This neighbour is on screen, grab its cell.
neighbour = this.cellList[coords[0]][coords[1]];
}
}
...
Note: Alive ShadowCells will not be garbage collected as they get stored in an Array with the other cells (I am certain of this from my debugging, see the cell count in your console output and count the visible cells).
For some reason the ShadowCell class appears to cause incorrect reporting of neighbours. I have attempted to debug it by following the creation, deletion and counted neighbours of each individual cell during each generation but my brain dies before it can put it all together. For all my debugging efforts I can't see why this behaviour should occur . ShadowCell is pretty much the same as a Cell to everything else that uses it (they use the exact same position functions .etc), the fact it doesn't get rendered shouldn't be the cause of this.
For generation 4 I get the following output by logging the creation of shadow maps, I can see that each is being created once per generation (note: The class doesn't show because I used utils.extend() to create a snapshot of them):
Object {x: 5, y: -1, key: "5,-1", neighbourCoords: Array[8], neighbours: 0…}
Object {x: 6, y: -1, key: "6,-1", neighbourCoords: Array[8], neighbours: 0…}
Object {x: 7, y: -1, key: "7,-1", neighbourCoords: Array[8], neighbours: 0…}
Object {x: 4, y: -1, key: "4,-1", neighbourCoords: Array[8], neighbours: 0…}
Object {x: -1, y: 1, key: "-1,1", neighbourCoords: Array[8], neighbours: 0…}
Object {x: -1, y: 2, key: "-1,2", neighbourCoords: Array[8], neighbours: 0…}
Object {x: -1, y: 3, key: "-1,3", neighbourCoords: Array[8], neighbours: 0…}
Object {x: 5, y: -2, key: "5,-2", neighbourCoords: Array[8], neighbours: 0…}
Object {x: 6, y: -2, key: "6,-2", neighbourCoords: Array[8], neighbours: 0…}
Object {x: 7, y: -2, key: "7,-2", neighbourCoords: Array[8], neighbours: 0…}
Object {x: -1, y: 4, key: "-1,4", neighbourCoords: Array[8], neighbours: 0…}
Logged on line 152 like so:
if (!shadowCellHash[key]) {
neighbour = shadowCellHash[key] = new ShadowCell(coords[0], coords[1]);
neighbour.neighbourCoords = cell.neighbourCoords;
console.log(utils.extend({}, neighbour));
} else {
shadowCellHash is not initialized with all of the ShadowCells before you start looping through every cell looking for neighbours. When the loop checks [5,-1] for neighbors, it doesn't find [6,-1] because it's not in shadowCellHash. Since [6,-1] is not found, a new dead [6,-1] is created, and [5,-1] is not born because it does not have enough live neighbours.
I think I've resolved your issue by eagerly re-populating shadowCellHash at the beginning of each World.tick
JSFiddle
// This hash goes out of scope after each tick allowing any dead shadowCells to be garbage collected
if (!this.settings.wrap) {
var shadowCellHash = {};
for (var i = 0; i < this.lifeList.length; i++) {
var cell = this.lifeList[i];
if (cell.key) {
shadowCellHash[cell.key] = cell;
}
}
}