I'm building a graph from the following datatype: points = [insulin:20, cho:30];.
Im using ng-repeat to loop through my array.
Im using the index as x-values and insulin or cho as y values.
I want the index as x-values because later on I will use dates as x-value and want to display cho/insulin values if they occur on that date. Values are bound to indices as they need to be shown at that timeframe in the svg
<line class="insulinLine" ng-repeat="point in points | limitTo :
points.length-1"
ng-x1="{{((width /points.length) * $index ) + (width/points.length)}}"
ng-y1="{{((point.insulin / maxY) * 400)}}"
ng-x2="{{((width / points.length) * ($index + 1)) + (width/points.length)}}"
ng-y2="{{((points[$index + 1].insulin / maxY) * 400)}}"/>
My problem occurs at null values, if there is a null value i'd like ng-repeat to skip the index of y2 till the next non-null value so that the line is connected to the next actual value.
My tried options are:
do nothing, this sets the y value to the bottom of the graph
ng-if, not displaying null values will just not build the lines
if="point.insulin!=null"
using ternary operators, messes up the whole graph, which is logical because it only evaluates at each point and doesn't move the index globally.
points[$index + 1].insulin !== null ? ((points[$index + 1].insulin / maxY) * 400) : ((points[$index + 2].insulin / maxY) * 400)
You could put the x1,x2,y1,y2 values on each seperate point, using a function inside javascript to determine them for every line, but there should be an easier way?
Could you make a conditional where you look for the next non-null value and then use that and skip the index to that value?
codepen link:
https://codepen.io/mbezema/pen/bvWPVm
I would just do this logic in the controller.
Have a setPoints methods, and each time you want to update the points array just go through that method.
Something like:
function ignoreNulls (array) {
let lastValue = null;
for (let i = array.length; i >= 0; i--) {
if (!array[i]) {
array[i] = lastValue;
}
else {
lastValue = array[i];
}
}
}
What I am doing here, is going backwards in the array, checking if there is a value. If there is - cache it. If there isn't put the last cached value instead.
Calculating the points in javascript and constructing a new array does the trick. I then loop through the insulinLines and choLines arrays to set up the final lines.
codepen link: https://codepen.io/mbezema/pen/bvWPVm
function determineLines() {
var insulinStack = [];
var choStack = [];
for (var i = 0; i < $scope.points.length; i++) {
if($scope.points[i].insulin !== null){
console.log(i);
insulinStack.push({
xValue: i,
insulin: $scope.points[i].insulin
});
}
if($scope.points[i].cho !== null){
choStack.push({
xValue: i,
cho: $scope.points[i].cho
});
}
}
$scope.insulinLines = [];
$scope.choLines = [];
while (insulinStack.length){
$scope.insulinLines.push(insulinStack.pop());
}
while (choStack.length){
$scope.choLines.push(choStack.pop());
}
}
Related
I am doing leetcode #11 Container With Most Water
https://leetcode.com/problems/container-with-most-water/
Given n non-negative integers a1, a2, ..., an , where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.
Note: You may not slant the container and n is at least 2.
var maxArea = function (height) {
var list = [];
for (var index = 1; index <= height.length; index++) {
var eachCorr = {
x_corr: index,
y_corr: height[index - 1]
}
list.push(eachCorr);
}
var mainResult = reCursion(list, list.length-1,0,1);
console.log(list);
console.log(mainResult);
return mainResult;
//last vertical line * each vertical line from index=1;
//x-corr*(last vertical - each vertical), y-corr*(smaller vertical line)
};
function reCursion(arr, index, x,y) {
//lastX and lastY use recursion to loop
var lastX = arr[index][x];
var lastY = arr[index][y];
var chosenY = 0;
var area = 0;
var result = [];
var maxAreaAns = 0;
for (var i = index - 1; i >= 0; i--) {
if (lastY > arr[i][1]) {
chosenY = arr[i][1];
} else {
chosenY = lastY;
}
area = (lastX - arr[i][0]) * chosenY;
console.log(`area = ${area} with i = ${i}, lastX=${lastX}, lastY=${lastY}`);
result.push(area);
}
if (index === 0) {
maxAreaAns = Math.max(...result);
return maxAreaAns;
} else {
return reCursion(arr, index - 1,0,1);
}
}
My approach is using recursion, first select the last vertical line, multiple to x-corr difference of
each vertical line before, then select the small y-corr of the vertical line when compared.
area = (x-corr difference of last vertical line and compared vertical line) * (y-coor of small vertical line)
Then use recursion to select the second last vertical line and so all until select the first vertical line.
Then I push all the area result into a array and find the maximum.
I want to know why this method can not execute( lastX, lastY, area variables are undefined).
Having analyzed your code, your
var lastX = arr[index][x];
var lastY = arr[index][y];
are both always undefined. Since arr[index] returns an object and not a list, you cannot get the values by indexing. You'll need to do
var lastX = arr[index].x_corr;
var lastY = arr[index].y_corr;
Which also goes for your
if (lastY > arr[i][1]) {
chosenY = arr[i][1];
Now you might have realized that your function always logs out -Infinity as its result.
This is because when the condition
if (index === 0) {
maxAreaAns = Math.max(...result);
return maxAreaAns;
}
is met and the code inside it is executed, the result array is always empty (try invoking the Math.max() function without any input. It will return -Infinity).
This is because when the index variable is equal to 0, the loop
for (var i = index - 1; i >= 0; i--) {
if (lastY > arr[i][1]) {
...
}
will not run (as i starts from -1), and the result stays as an empty array.
I am guessing that what you would want to do is to either set result array as a global variable, or to pass it to the next reCursion() function.
That being said, I actually don't see the point of solving this problem using recursion.
Instead of using recursion (which obviously makes it difficult to write and understand the code), why not just use a nested loop to check the combinations?
I am trying to fetch a tile's neighbour in a hexagonal grid.
Both grids and tiles are React components, and I have a method handy in my Grid component to find all of a tile's neighbours
The method works fine for neighbours within the index, and I set up a modulus to wrap around to the other side of the grid if a tile was to get out of bounds. Those indexes return NaN.
/**
* Returns all neighbouring tiles of this tile
*/
getTileNeighbours(tile) {
// Checks all arrays in the two-dimensional grid if this tile exists
for (let i in this.state.grid) {
let array = this.state.grid[i]
for (let j in array) {
console.log("Looking...")
let item = array[j]
if (item.state.name === tile.state.name) {
console.log("Found you!")
// Gets the position of the tile
let j = array.indexOf(tile)
//console.log(`Tile was found at position [${i}, ${j}]. Returning neighbours.`)
let neighbours = []
// All possible permutations of neighbours
let positions = [
{i:0,j:-2}, {i:1,j:-1}, {i:1,j:1}, {i:0,j:2}, {i:-1,j:1}, {i:-1,j:-1}
]
// If neighbouring indexes are out of bounds, wraps around to the other edge of the arrays
for (let k in positions) {
let position = positions[k]
let xIndex = (i + position.i) % this.state.grid.length
let yIndex = (j + position.j) % array.length
console.log(`Resolving '(${i} + ${position.i}) % ${this.state.grid.length}': ${(i + position.i) % this.state.grid.length}`)
console.log(`Actual indexes used: 'this.state.grid[${xIndex}][${yIndex}]'`)
let match = this.state.grid[xIndex][yIndex]
if (match) neighbours.push(match)
}
return neighbours
}
}
}
}
Here's a capture of my JavaScript console in Chrome
I figured out what went wrong, it all came down to the variable types not all being numbers. i, j and k are all strings (since they are used in a for (x in y) context), at k = 5, we end up with i + position.i being equal to 0-1 as a string, which cannot be used with a modulus. By forcing i and j to be numbers, we no longer encounter our NaN.
Similarly, using the modulus to wrap around was a bad idea altogether.
It has been replaced with a ternary to add the length of the array when the resulting index was negative
Two lines were all I needed to change, here they are
let xIndex = Number(i) + position.i < 0 ? Number(i) + position.i + this.state.grid.length : Number(i) + position.i
let yIndex = Number(j) + position.j < 0 ? Number(j) + position.j + array.length : Number(j) + position.j
This was all due to a gross lack of attention on my end, I should have troubleshooted better.
Thats my code:
var randomCoord = function(cells) {
var step = $('.workplace').innerWidth()/cells;
var xCord = (Math.floor(Math.random() * (cells+1)))*step;
var yCord = (Math.floor(Math.random() * (cells+1)))*step;
if(plants.length != 0) {
for (var i=0; i<plants.length; i++) {
if (plants[i].left != xCord && plants[i].top != yCord) {
plants.push({"top": yCord, "left": xCord});
}
}
} else {
plants.push({"top": yCord, "left": xCord});
}
};
var multiplayer = function(func, endIteration, cells) {
for (var i=0; i<endIteration; i++) {
func(cells);
};
};
multiplayer(randomCoord, 5, 10) // will iterate diferent times
Function, multiplayer have to run "randomCoords" 5 times, but it's not working. Why quantity of iteration is uncontroled? How can I fix it?
It looks like your for loop in randomCoord() is supposed to be only pushing an entry into the array if the coordinates don't already exist in the array, but that isn't how your logic works. Instead, you check each and every item in the array and if it's not equal to that item in the array, you push it and you do that for each item in the array so you end up with lots of duplicates in the array (exactly what you're trying to prevent).
So, the first time you call randomCoord, you get one item. The next time you call it, you get two items. The third time you call it you get 4 items, then 8, then 16. This is a fairly simple logic error.
If you just want to add one unique item each time you call randomCoord, then you could use logic like this:
var randomCoord = function(cells) {
var step = $('.workplace').innerWidth()/cells;
var xCord = (Math.floor(Math.random() * (cells+1)))*step;
var yCord = (Math.floor(Math.random() * (cells+1)))*step;
var found = false;
for (var i=0; i<plants.length; i++) {
if (plants[i].left == xCord && plants[i].top == yCord) {
found = true;
break;
}
}
if (!found) {
plants.push({"top": yCord, "left": xCord});
}
};
Note, you don't need the separate if (plants.length != 0) because the for loop already checks that and our new found variable handles the case where the array is initially empty.
If you happen to generate a coordinate conflict, this will add no item on that function call though the odds of generating two conflicting random values are fairly low as long as cells*step is a decent size number (the range of your random number generator). If you want to try it again in that case, then you need another loop to try again if a conflict is found.
I have a list of data in javascript that looks like this:
[[152, 48, 'http://www.google.com'],
[198, 47, 'http://www.stackoverflow.com'],
[199, 45, 'http://www.apple.com']]
I am using flot to create a plot, and am trying to pass this third value to access a hyperlink from the point. As such, I am trying to lookup the third value of each list by using the first two as the lookup keys (i.e., [[x,y,hyperlink],[x2,y2,hyperlink2]], click on a point, then use the appropriate (x,y) to find the corresponding hyperlink)
Is there anyway to do this, or do I need to pass some dictionaries for x and y to javascript, then find the common variable from the two lists that were looked up? In python I know you could do a filter of list on the x value with itemgetter, then lookup a link corresponding to the y value. But I know almost nothing about js, so could a solution to ID-ing with (x,y) be given, or if not possible or advised, then a solution to taking two lists of (from x and y vals) and find a common value (if multiple, just one, anyone)?
You can make use of the Array .filter() method to figure out if any elements match the supplied x and y. (Note that IE didn't support .filter() until version 9, but MDN has a shim that you can include).
var data = [[152, 48, 'http://www.google.com'],
[198, 47, 'http://www.stackoverflow.com'],
[199, 45, 'http://www.apple.com']];
function getURLForPoint1(x, y) {
var p = data.filter(function(el) {
return (el[0] === x && el[1] === y);
});
if (p.length === 1) return p[0][2];
// else 0 or more than 1 elements mathced so return undefined
}
Alternatively you can create a dictionary object up front and then do future lookups from the dictionary:
var getURLForPoint2 = function() {
var dataDictionary = {}, i;
for (i = 0; i < data.length; i++)
dataDictionary[data[i][0]+" "+data[i][1]] = data[i][2];
return function(x, y) {
return dataDictionary[x + " " + y];
};
}();
Either way I've coded it so that if you ask for a point that isn't in the list you'll get undefined back, but obviously you can change that to return an empty string or throw an exception or whatever you like.
alert(getURLForPoint1(198, 47)); // 'http://www.stackoverflow.com'
alert(getURLForPoint2(198, 47)); // 'http://www.stackoverflow.com'
alert(getURLForPoint2(4, 5)); // undefined
Demo of both: http://jsfiddle.net/WdSAz/
Sorry, no shortcut way to do it in js except to just loop through the list and find the one that has the matching "x" and "y" value.
However, depending on how large your list is (and whether or not this list will be used for something else...) you could restructure the data to make it more efficient. For instance, do a structure like (assumed possible to have for instance x1, y1 vs x1, y2)
x1 > y1 > url
x1 > y2 > url
x2 > y1 > url
etc...
then you can immediately jump to the 2nd lvl "y" list by the "x" index, and the only looping would be how many "y" values share the same "x" value
edit:
actually if you wanna take it a step further with reorganizing the data, you could do something like this:
<script type='text/javascript'>
var list = {
1 : {
1 : 'foobar 1,1',
2 : 'foobar 1,2'
},
2 : {
1 : 'foobar 2,1',
2 : 'foobar 2,2'
},
};
</script>
which will allow you to do for instance this
var x = 1;
var y = 2;
alert(list[x][y]);
somthing like this maybe
var findX = 198
var findY = 47
var targetUrl
for (var i=0; i<arr.length; i++)
{
for (var j=0; j<arr[i].length; j++)
{
if (findX = j[0] && findY == j[1])
{
targetUrl = j[2]
}
}
}
i'm getting the error
Uncaught TypeError: Cannot set property '0' of undefined
for some reason in this line
world_map_array[i][z]="grass.gif|ongrass.gif|collision.gif|above.gif";
Why is this happening?
thanks for any help
var x_world_map_tiles = 100;
var y_world_map_tiles = 100;
var world_map_array = new Array(x_world_map_tiles);
for (i=0; i<=2; i++)//create a two dimensional array so can access the map through x and y coords map_array[0][1] etc.
{
world_map_array[i]=new Array(y_world_map_tiles);
}
for (i=0; i<=x_world_map_tiles; i++)//just a test
{
for (z=0; z<=y_world_map_tiles; z++)//just a test
{
world_map_array[i][z]="grass.gif|ongrass.gif|collision.gif|above.gif";
}
}
Arrays in JavaScript have quirks of their own that you may not be expecting if you come from other languages. Two important ones for your use case are:
You cannot directly declare multidimension arrays in JavaScript.
There's little efficiency benefit (and no added safety) when you set the size of the array at creation.
Unlike other languages, JavaScript won't allocate a block of memory for the full array.
(It doesn't know what kind of objects you're going to be putting in each cell,
and therefore how much total memory it will need.)
Instead, all the size argument to Array() does for you is set the array's length property.
For the general, 2d array case, I'd suggest:
Create the "top" array, e.g.:
var i // the first-order index in a
, j // the second order index in a
, a = []
Initialize array elements as needed.
This is called lazy initialization,
and, in this case, it simply involves testing that a[i] exists
before we try to assign something to a[i][j], e.g.:
if (!a[i]) a[i] = []
In English the above statement reads:
"If the i-th element of a is 'falsy', assign an empty array to the i-th element."
Finally, assign the actual value to the multideminsional array:
a[i][j] = 'whatever'
For your case, you know the values ahead of time,
so you can initialize each element in advance.
(If you're not overriding most of the elements, however,
a lazy implementation may be better; see below.)
var x, x_length = 100
, y, y_length = 100
, map = []
// Don't be lazy
for (x = 0; x < x_length; x++) {
map[x] = []
for (y = 0; y < y_length; y++) {
map[x][y] = 'grass.gif|ongrass.gif|collision.gif|above.gif'
}
}
As some others have said,
an array with 100 elements has indexes numbered from zero to ninety-nine,
so a less-than comparison is most appropriate here.
For reference, here's an implementation that uses lazy initialization.
I've gone with a function interface instead of directly accessing the array;
it's longer and more complex, but also more complete.
The initialization pattern I've used here is called an
immediately invoked function expression.
If you haven't seen it before,
it's one of the more useful JavaScript patterns
and well worth taking some time to understand.
var map = (function (x_length, y_length, v_default, undefined) {
// Unless v_default is overwritten, use ...
v_default = v_default || 'grass.gif|ongrass.gif|collision.gif|above.gif'
// Private backing array; will contain only values for a[x][y]
// that were explicitly set.
var a = []
// Private helper function.
// - Returns `true` if `x` is between `0` and `x_length - 1`
// and `y` is between `0` and `y_length - 1`.
// - Returns `false` otherwise.
function valid (x, y) {
return (x >= 0
&& x < x_length
&& y >= 0
&& y < y_length)
}
// Private helper function.
// - Returns `true` if a[x][y] has been set().
// - Returns `false` otherwise.
function exists (x, y) {
return !!a[x] && !!a[x][y]
}
// Private getter
// - Returns the value of a[x][y] if it has been set().
// - Returns `undefined` if the point (x,y) is invalid.
// - Returns `v_default` otherwise.
function get (x, y) {
if (!valid(x, y)) return undefined
else if (exists(x, y)) return a[x][y]
else return v_default
}
// Private setter
// - Returns the value set on success.
// - Returns `undefined` on failure
function set (x, y, v) {
if (valid(x, y)) {
// We're being lazy
if (!a[x]) a[x] = []
a[x][y] = v
return a[x][y]
}
return undefined
}
// Return an interface function.
// - Pass the function three arguments, (x, y, v), to set a[x][y] = v
// - Pass the function two arguments, (x, y), to get a[x][y]
return function (x, y, v) {
if (arguments.length > 2) {
return set(x, y, v)
} else {
return get(x, y)
}
}
})(100, 100)
When I ran the above in node, the following tests printed sensible values:
// Invalid invocations
console.log('map() : %s', map())
console.log('map( 0) : %s', map(0))
console.log('map( -1, 0) : %s', map(-1,0))
console.log('map( 0, -1) : %s', map(0, -1))
console.log('map( -1, -1) : %s', map(-1, -1))
// Valid invocations
console.log('map( 0, 0) : %s', map(0, 0))
console.log('map( 99, 99) : %s', map(99, 99))
console.log('map( 1, 1) : %s', map(1,1))
console.log('map( 1, 1, "foo") : %s', map(1,1, 'foo'))
console.log('map( 1, 1) : %s', map(1,1))
var x_world_map_tiles = 100;
var y_world_map_tiles = 100;
var world_map_array = new Array(x_world_map_tiles);
for (i=0; i<=2; i++)//create a two dimensional array
{
world_map_array[i]=new Array(y_world_map_tiles);
}
for (i=0; i<x_world_map_tiles; i++)
{
for (z=0; z<y_world_map_tiles; z++)
{
world_map_array[i][z]="grass.gif|ongrass.gif|collision.gif|above.gif";
}
}
As your array has a length of 100, you must go from 0 to 99 (<100) and not to 100 (<=)
This
for (i=0; i<=2; i++)
must be:
for (i=0; i<=x_world_map_tiles ; i++)
You're feeding the world_map_array[i] expression a value for i that does not exist in
world_map_array. So I guess x_world_map_titles is > 2.
I think you need to rewrite i<=2 to i<=x_world_map_titles
Also you do not need to specify the size of the array. I would just use literals in this case:
var x_world_map_tiles = 100;
var y_world_map_tiles = 100;
var world_map_array = [];
for (i=0; i<=x_world_map_tiles; i++)
//create a two dimensional array of 101x101 so can access the map through x and y coords map_array[0][1] etc. {
world_map_array[i]=[];
}
for (i=0; i<=x_world_map_tiles; i++)//just a test {
for (z=0; z<=y_world_map_tiles; z++)//just a test {
world_map_array[i][z]="grass.gif|ongrass.gif|collision.gif|above.gif";
}
}
Getting Uncaught TypeError while using 2-D array in javascript.
For two- dimension array, first declare parent array
var arryTwoDimension= [];
Then depending on the situation, we can create child array by
arryTwoDimension[i]=[] i will be from 0,1,2......
This will solve the issue.