Conway's Game of Life - beyond the grid - javascript

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;
}
}
}

Related

Sorting an array of object

I wish to sort an array of medals. My first sort returns an array sorted according to the gold medals. I then wish to range those which are having the same gold but silver medals are different (same for bronze). I use the following codes that actually makes me run out of memory. This is my code:
static sort(data) {
let sorted = data.sort((a, b) => b.medal.gold - a.medal.gold);
let next, temp, current;
for (let i = 0; i < sorted.length; i++) {
current = sorted[i].medal;
if (sorted[i+1]) next = sorted[i+1].medal;
if (next) {
if (current.gold === next.gold) {
if (current.silver < next.silver) {
temp = sorted[i+1];
sorted[i+1] = sorted[i];
sorted[i] = temp;
}
else if (current.silver === next.silver) {
if (current.bronze < next.bronze) {
temp = sorted[i+1];
sorted[i+1] = sorted[i];
sorted[i] = temp;
}
}
}
}
}
return sorted;
}
You'll want to improve your compare function so it takes care of that requirement:
data.sort((a, b) => (b.medal.gold - a.medal.gold)
|| (b.medal.silver - a.medal.silver)
|| (b.medal.bronze - a.medal.bronze) )
And then you don't need the (endless) for loop at all.
You have to set next to null somewhere, because it keeps the value from the previous iteration and the if(next) is always true. Afterwards the function will always create one more element and add it in the array (sorted[i+1] = sorted[i]) until you run out of memory.
Here is a working example:
var rawData =
[{ id: 1, medal: {gold: 2, silver: 1, bronze: 1}},
{ id: 2, medal: {gold: 2, silver: 1, bronze: 2} },
{ id: 3, medal: {gold: 5, silver: 1, bronze: 4} } ];
function sortData(data) {
let sorted = data.sort((a, b) => b.medal.gold - a.medal.gold);
let next, temp, current;
for (let i = 0; i < sorted.length; i++) {
next = undefined;
current = sorted[i].medal;
if (sorted[i+1]) next = sorted[i+1].medal;
if (next) {
if (current.gold === next.gold) {
if (current.silver < next.silver) {
temp = sorted[i+1];
sorted[i+1] = sorted[i];
sorted[i] = temp;
}
else if (current.silver === next.silver) {
if (current.bronze < next.bronze) {
temp = sorted[i+1];
sorted[i+1] = sorted[i];
sorted[i] = temp;
}
}
}
}
}
return sorted;
};
console.log(sortData(rawData))
Please note that in the function you are using medal instead of medals as the data you have provided in one of your comments.

Target specific attributes with merge sort

Implemented the merge sort algorithm in my javascript code.
I'm wonder how I can target specific attributes like date, title, name etc for sorting in an array when calling merge sort like mergeSort(array);.
function mergeSort(arr){
var len = arr.length;
if(len <2)
return arr;
var mid = Math.floor(len/2),
left = arr.slice(0,mid),
right =arr.slice(mid);
return merge(mergeSort(left),mergeSort(right));
}
function merge(left, right){
var result = [],
lLen = left.length,
rLen = right.length,
l = 0,
r = 0;
while(l < lLen && r < rLen){
if(left[l] < right[r]){
result.push(left[l++]);
}
else{
result.push(right[r++]);
}
}
return result.concat(left.slice(l)).concat(right.slice(r));
}
Using it in a sort options method. What I want is to print a sorted list. The way the list is sorted will be defined by the users chosen sort option.
function sortConfig(array, sortOption){
if(sortOption == 'title') mergeSort(array.Title);
//..etc
}
To implement the behavior with an optional argument, you could do it in the following way:
function mergeSort(arr, compare = (item => item))
This would set compare function to be the item itself when running the merge
and then we update the calling of the merge and mergeSort itself, where they now all get the compare argument
return merge(mergeSort(left, compare), mergeSort(right, compare), compare);
and ofcourse the declaration for your merge function itself
function merge(left, right, compare)
Which then calls the compare function upon comparison, like here:
if (compare(left[l]) < compare(right[r]))
This lets you choose wether you wish to give an argument or not wen you call your mergeSort function, like:
console.log(mergeSort(nrs).join(','));
console.log(mergeSort(nrs, n => -n).join(','));
console.log(mergeSort(arr, i => i.id));
console.log(mergeSort(arr, i => i.title));
function mergeSort(arr, compare = (item => item)) {
var len = arr.length;
if (len < 2)
return arr;
var mid = Math.floor(len / 2),
left = arr.slice(0, mid),
right = arr.slice(mid);
return merge(mergeSort(left, compare), mergeSort(right, compare), compare);
}
function merge(left, right, compare) {
var result = [],
lLen = left.length,
rLen = right.length,
l = 0,
r = 0;
while (l < lLen && r < rLen) {
if (compare(left[l]) < compare(right[r])) {
result.push(left[l++]);
} else {
result.push(right[r++]);
}
}
return result.concat(left.slice(l)).concat(right.slice(r));
}
var arr = [{
title: 'test 5',
id: 4
}, {
title: 'test',
id: 0
}, {
title: 'test 3',
id: 2
}, {
title: 'test 4',
id: 3
}];
var nrs = [5, 3, 7, 156, 15, 6, 17, 9];
// and call like
console.log(mergeSort(nrs).join(','));
console.log(mergeSort(nrs, n => -n).join(','));
// or like
console.log(mergeSort(arr, i => i.id));
console.log(mergeSort(arr, i => i.title));
For the sake of brevity, these examples show how to sort an array of objects based on a property with a string value. You would most likely need to create some additional logic to handle different types of properties.
1. Array.sort()
You can do this with the Array.sort() method
Fiddle Example
myThings = [
{ alpha: 'a' },
{ alpha: 'x' },
{ alpha: 'p' },
{ alpha: 'orange' },
{ alpha: 'c' },
{ alpha: 'w' }
];
myThings.sort(function(a, b) {
var alphaA = a.alpha.toUpperCase();
var alphaB = b.alpha.toUpperCase();
if (alphaA < alphaB) return -1;
if (alphaA > alphaB) return 1;
return 0;
});
console.log(myThings);
2. Or, compare array item property value instead of array item value
Fiddle Example
function mergeSort(arr, prop) {
if (arr.length < 2)
return arr;
var middle = parseInt(arr.length / 2);
var left = arr.slice(0, middle);
var right = arr.slice(middle, arr.length);
return merge(mergeSort(left, prop), mergeSort(right, prop), prop);
}
function merge(left, right, prop) {
var result = [];
while (left.length && right.length) {
if (left[0][prop] <= right[0][prop]) {
result.push(left.shift());
} else {
result.push(right.shift());
}
}
while (left.length)
result.push(left.shift());
while (right.length)
result.push(right.shift());
return result;
}
myThings = [
{ alpha: 'a' },
{ alpha: 'x' },
{ alpha: 'p' },
{ alpha: 'orange' },
{ alpha: 'c' },
{ alpha: 'w' }
];
console.log(mergeSort(myThings, 'alpha'));

Shortest path algorithm js error

I am quite new into JS and a friend of mine sent me this fiddle
function shortestPath(g, s) {
g.vertexes.forEach(function(u) {
u.dist = Infinity;
u.prev = null;
});
s.dist = 0;
for (var i = 0; i < g.vertexes.length - 1; i++) {
g.edges.forEach(function(e) {
update(e);
});
}
printResult(); }
function update(e) {
var u = e.from;
var v = e.to;
if (v.dist > u.dist + e.data) {
v.dist = u.dist + e.data;
v.prev = u;
} }
var result = [];
function printResult() {
var str = '';
debugger;
for (var i = 0; i < result[0].length; i++) {
for (var j = 0; j < result.length; j++) {
str += result[i][j] + ' ';
}
console.log(str);
str = '';
} }
function printGraph(G) {
var a = [];
G.vertexes.forEach(function(u) {
a.push(u.dist);
});
result.push(a); }
function Graph(options) {
options = options || {};
this.directed = (options.directed != null) ? options.directed : true;
this.vertexes = [];
this.edges = []; }
Graph.prototype.vertex = function(name) {
var v = {
adjacent: [],
name: name.toString()
};
this.vertexes.push(v);
return this; };
Graph.prototype.get = function(name) {
return this.vertexes.filter(function(el) {
return el.name === name.toString();
})[0]; };
Graph.prototype.edge = function(a, b, w) {
var that = this;
connect(a, b, w);
if (!this.directed) {
connect(b, a, w);
}
function connect(a, b, data) {
var u = that.vertexes.filter(function(el) {
return el.name === a.toString();
})[0];
var v = that.vertexes.filter(function(el) {
return el.name === b.toString();
})[0];
u.adjacent.push(v);
that.edges.push({
from: u,
to: v,
data: data
});
}
return this; };
function main() {
var g = new Graph();
g.vertex(1)
.vertex(2)
.vertex(3)
.vertex(4)
.vertex(5)
.vertex(6)
.vertex(7)
.vertex(8);
g.edge(1, 2, -2);
g.edge(1, 5, -2);
g.edge(1, 6, -3);
g.edge(1, 8, -1);
g.edge(2, 6, 7);
g.edge(2, 8, 4);
g.edge(3, 2, 2);
g.edge(3, 4, 5);
g.edge(3, 7, 9);
g.edge(4, 7, 4);
g.edge(5, 7, 5);
g.edge(7, 8, -1);
g.edge(8, 2, 2);
g.edge(8, 5, 8);
g.edge(8, 6, 3);
g.edge(8, 7, 7);
shortestPath(g, g.get(3));
console.log(g); }
main();
(Shortest path Bellman-Ford)
and don't really get it why it throws the error property 'length' of undefined in the console.
Any advice how to fix this error?
In general, when JS complains Cannot read property "xxx" of undefined, that means that somewhere you have foo.xxx and foo is the JavaScript value undefined (which is not an object, and has no properties).
Track such a problem down by finding the line in question (using debugging tools, or even just looking for .length in your case) and considering: when might the variable in question be undefined?
In your specific case, the error occurs on this line:
for (var i = 0; i < result[0].length; i++) {
which means that result[0] is undefined. Which means that your result array has no value at [0]. It is empty.
Since the printResult function is called from one place (line 59), this likely means that result is still an empty array when printResult() is called. You can confirm this by setting a breakpoint at that location and examining what result is.
As for why it is empty:
The only code that affects the result array is result.push(a) in the printGraph() function. And this function is never called. Ask your friend why s/he defined printGraph() but never invoked it.
It may be as simple as calling printGraph(g) right before printResult().

How to make a function that contains a promise return a value instead of a promise?

Say, I have a function F1 that will be called in many other function. F1 is meant to return a value VAL that will be used in F2. A promise is needed to retrieve that needed data that will help calculate VAL. Having F1 as a promise would cause a lot of confusion in F2, for F1 is often called inside IF statements and FOR loops. Let me illustrate this scenario:
function F1(param1, param2) {
var VAL = 0;
promise(param1, param2).then(function(data) {
for (var i = 0; i < data.length; i++) {
// Do some calculation here
}
});
return VAL;
}
function F2(x1, x2) {
var myArray = [],
someValue = 0;
if ([conditional expression]) {
someValue = F1(x1, x2);
call_some_function();
myArray.push({
val: someValue,
...
});
}
var x = someValue + y;
myArray.push({
id: x,
...
});
return myArray;
}
How do I make sure that F1 returns VAL (integer) so I can use it as a synchronous function?
Thanks in advance for your help.
EDIT:
Here is how the code looks like:
function myFunc(x, y) {
return init()
.then(function() {
return getData(x, y).then(function(data) {
if (data.length == 0) return [];
var id, name,
firstPass = true,
headIn = 0,
headOut = 0,
currentHead = 0,
payWtIn = 0,
payWtOut = 0,
expectedAdg = 0,
weight = 0,
results = [];
for (var i = 0; i < data.length; i++) {
if (firstPass) {
id = data[i].id();
name = data[i].name();
headIn = data[i].headIn();
headOut = data[i].headOut();
expectedAdg = data[i].expectedAdg();
firstPass = false;
}
if (id != data[i].id()) {
buildFunc();
reset();
}
headIn += data[i].headIn();
headOut += data[i].headOut();
payWtIn += data[i].payWtIn();
payWtOut += data[i].payWtOut();
}
buildFunc();
return results;
function buildFunc() {
currentHead = headIn - headOut;
var headDays = getHeadDays({ locationId: locationId, groupId: groupId, callDate: null });
var totalWeight = headIn != 0
? ((((headDays * expectedAdg) + payWtIn) / headIn) * currentHead) + payWtOut
: 0;
results.push({
id: id,
name: name,
headIn: headIn,
headOut: headOut,
headDays: headDays,
currentHd: currentHead,
totalWt: totalWeight
});
}
function reset() {
id = data[i].id();
name = data[i].name();
headIn = data[i].headIn();
headOut = data[i].headOut();
expectedAdg = data[i].expectedAdg();
payWtIn = 0;
payWtOut = 0;
weight = 0;
}
});
});
}
function getHeadDays(params) {
var VAL = 0;
promise(params.a, params.b).then(function(data) {
for (var i = 0; i < data.length; i++) {
// Make calculation to determine VAL here
}
});
return VAL;
}
The init function loads needed entities in the cache (I'm working with BreezeJs) for querying. The getData function gets raw data that are sorted by id from database, and those data are used to determine the results array. As the data are looped through, as long as the id of each record is the same, headIn, headOut, payWtIn and payWtOut are incremented by the record fields, and when the previous and current id are different, we can calculate totalWeight and push a new record to the results array with the buildFunc function. Inside that buildFunc function, we retrieve the headDays in order to calculate totalWeight. The getHeadDays function will be called in many other functions. Please, let me know if you have any questions. Thanks in advance for your help.
You can't.
If you need to return a promise, then that is because the value won't be available until some event happens, and the function will (or at least may) return before then. That's the point of promises.

Javascript - Checking if a number is closer to 2 variables

I have 12 different numbers stored in variables, like this:
var span1 = 8.333333333;
var span2 = 16.66666667;
var span3 = 25;
var span4 = 33.33333333;
var span5 = 41.66666667;
var span6 = 50;
var span7 = 58.33333333;
var span8 = 66.66666667;
var span9 = 75;
var span10 = 83.33333333;
var span11 = 91.66666667;
var span12 = 100;
I have a function which compares a div's width to its parent's width and returns a value as a percentage (minus the % sign), for example, 48.5586. I'd like the function to check which of these span variables the result is closest to. For example, 48.5586 would return "span6" as it's closer to 50 than 41.666667.
Don't really know where to start with this one, any ideas?
Since each span is 8 1/3 % more than the previous one, which is just 100/12, you should be able to use a formula to work out which span class you need.
I worked out the following:
function spanClass(percentWidth) {
return 'span' + Math.round(percentWidth / (100/12));
}
This gives span6 for 48.5586.
The smallest difference can be found by taking the minimum of the absolute value of the difference between your input number and each variable's value.
That said, you should really use a proper data structure, like an object or an array, for mapping these strings to numbers.
Put the values in an array, then you can sort the array on the differece between the values. The smallest differece is first in the array, so that is the closest value:
var spans = [
{ span: 1, value: 8.333333333 },
{ span: 2, value: 16.66666667 },
{ span: 3, value: 25 },
{ span: 4, value: 33.33333333 },
{ span: 5, value: 41.66666667 },
{ span: 6, value: 50 },
{ span: 7, value: 58.33333333 },
{ span: 8, value: 66.66666667 },
{ span: 9, value: 75 },
{ span: 10, value: 83.33333333 },
{ span: 11, value: 91.66666667 },
{ span: 12, value: 100 }
];
var value = 48.5586;
spans.sort(function(x, y){
var a = Math.abs(value - x.value);
var b = Math.abs(value - y.value);
return a == b ? 0 : a < b ? -1 : 1;
});
alert(spans[0].span);
Demo: http://jsfiddle.net/Guffa/77Rf9/
Its better to have array. in my code also i am converting your variables to array and then processing. Eval is bad and should not be used
http://codebins.com/bin/4ldqp6b
var span1 = 8.333333333;
var span2 = 16.66666667;
var span3 = 25;
var span4 = 33.33333333;
var span5 = 41.66666667;
var span6 = 50;
var span7 = 58.33333333;
var span8 = 66.66666667;
var span9 = 75;
var span10 = 83.33333333;
var span11 = 91.66666667;
var span12 = 100;
var arr = [];
for (var i = 0; i < 12; i++) {
eval('arr[' + i + '] = span' + (i + 1));
}
function closestTo(arr, val) {
var closest = 10000,
ind = -1,
diff = 10000; // some random number
for (var i = 0; i < arr.length; i++) {
var tmp = Math.abs(val - arr[i]);
if (diff > tmp) {
diff = tmp;
closest = arr[i];
ind = i;
}
}
alert("Closesnt Number: " + closest + "\nClosest Index:" + ind + "\nclosest variable: span" + (ind + 1))
}
closestTo(arr, 50.12)
Implementation of a closest function in JavaScript. Moved all the spans into an object to make iterating over them easier. Just enumerate all the spans and keep the closest one.
var spans = {
span1: 8.333333333,
span2: 16.66666667,
span3: 25,
span4: 33.33333333,
span5: 41.66666667,
span6: 50,
span7: 58.33333333,
span8: 66.66666667,
span9: 75,
span10: 83.33333333,
span11: 91.66666667,
span12: 100
};
function closest(value) {
var delta
, lastMatch;
Object.keys(spans).forEach(function (span) {
var thisDelta;
if (!delta) {
lastMatch = span;
delta = Math.abs(value - spans[span]);
} else {
thisDelta = Math.abs(value - spans[span]);
if (thisDelta < delta) {
lastMatch = span;
delta = thisDelta;
}
}
});
return lastMatch;
}
var result = closest(48.5586);
console.log(result);
Here a working Codepen example.

Categories