Lookup value in a list of lists, using js - javascript

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

Related

angularjs ng repeat svg skipping index of null values

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

Why doesn't this for loop read this in passed argument properly?

Below is my code
function generateGrid(spacing, boundBox, geometry) {
console.log(spacing);
console.log(boundBox);
var grid = [];
console.log(spacing);
for (var x = boundBox[0]; x < boundBox[2]; x = x + spacing) {
for (var y = boundBox[1]; y < boundBox[3]; y = y + spacing) {
if(geometry.intersectsCoordinate([x, y])) grid.push([x, y]);
console.log(boundBox[3] - y)
}
console.log(boundBox[1] - x)
}
console.log(grid);
}
If spacing is replaced by a number like 10000 the for loop executes fine.
From your Console screenshot it looks like the passed in argument is the string "10000" rather than the number 10000.
Either check the code that's calling your function, or convert to an integer inside the function, for example by using parseInt(spacing).
As a tip to help with spotting any similar issues in the future, Chrome's console.log shows numeric values in blue and string values in black.
x is a number so use String(x) so you use operator + between two strings, that would give you "15" + "1" = "151", but that is probably not what you wanted

javascript parse contents using $.each and create an array to send to function

I am trying to solve a problem of creating an array from json format data.
Here is an example of the data I have been trying to send to a function in the correct format.
{"polylines":[{"1":"-3555.00","2":"-2354.00","3":"-981.00","id":"1","text":"line1"},
{"1":"2564.25","2":"2566.00","3":"2664.50","id":"2","text":"line2"}]}
The data can be rearranged/changed/and made different, this just an example of what I have been trying to get to work. I used numbers to represent X and Y coordinates. I didn't really know what other way I could represent the data for multiple x and y coordinates, so I used odd and even to represent them.
The array I am trying to create is like this
[Array[4]]
0: Array[4]
0: Array[2]
0: -82.1002847583325
1: -98.19580078125
length: 2
1: Array[2]
0: -82.1002847583325
1: -98.19580078125
length: 2
[Array[5]]
0: Array[4]
0: Array[2]
0: -82.1002847583325
1: -98.19580078125
length: 2
1: Array[2]
0: -82.1002847583325
1: -98.19580078125
length: 2
0 is X and 1 is Y
As you see in the data, there is 2 lines and I need to send the X and Y coordinates from the 2 lines to the function separately.
This is my current source
$.getJSON("data.php", function(e) {
var x = [];
var y = [];
var xy = [];
// Get each polyline
$.each(e.polylines, function(t, n) {
//go through each x and y so we can convert the x and y in a separate function
$.each(n, function(n, r) {
// If odd
if(n % 2 == 1){
x.push(r);
// If even
} else if (n % 2 == 0){
y.push(r);
// Convert each coordinate
xy[r] = convertPoint(x, y);
x = []; // Clear x
y = []; // Clear y
xy.push(xy[r]);
}
});
console.log(xy);
// My problem is here, I cant seem to create the correct array
//because the code is just going through both lines and sending them together.
//I need to somehow separate them And send both arrays to the line below
//using the above each function.
var r = L.multiPolyline([xy], {
color: n.color,
clickable: false,
lineCap: "round"
}).addTo(map);
});
});
I have been trying to get my head round this for many many hours.. I have now ran out of ideas.
I would really like to know how to do this..
Instead of odd/even combination, you can create your json like below (array of objects)
var json = [{"X":"100","Y":"100"},{"X":"200","Y":"200"},{"X":"300","Y":"300"}];
and then process it using loop on it and access proper coordinates.
$(json).each(function(i){
var x = json[i].X;
var y = json[i].Y;
});
If you're using MVC with C# at Server side, you can create that json like below
Coordinate class
public class Coordinates
{
public string X { get; set; }
public string Y { get; set; }
}
your controller action code would be something like:-
List<Coordinates> lst = new List<Coordinates>();
lst.Add(new Coordinates { X = "100", Y = "100" });
lst.Add(new Coordinates { X = "200", Y = "200" });
lst.Add(new Coordinates { X = "300", Y = "300" });
lst.Add(new Coordinates { X = "400", Y = "400" });
var json = new JavaScriptSerializer().Serialize(lst);
ViewBag.Coordinates = json;
then in view part, access this json like below;
//var json = [{"X":"100","Y":"100"},{"X":"200","Y":"200"},{"X":"300","Y":"300"}];
var json = JSON.parse('#ViewBag.Coordinates');
I have managed to solve this myself after thinking over a bite to eat. Even though I did that yesterday lol.
Here is the fix...
var x = [];
var y = [];
var xy = [];
var pushed = [];
var pushed2 = [];
$.each(e.polylines, function(t, n) {
$.each(n, function(n, r) {
if(n % 2 == 1){
x.push(r);
} else if (n % 2 == 0){
y.push(r);
xy = convertPoint(x, y);
pushed.push(xy);
x = [];
y = [];
}
});
pushed2 = [pushed];
pushed = [];
console.log(pushed2);
var r = L.multiPolyline(pushed2, {
color: "#ffffff",
clickable: false,
lineCap: "round"
}).addTo(map);
I feel dumb sometimes... Lol Maybe I am..
After a little play with the arrays and understanding them ALOT more.
So basically xy is sent and converted, pushed saves xy into an array..
My problem was, I wasn't clearing the array after it had created the first line, so it was stacking the arrays up causing weird behaviour.
Its so annoying when its something so small lol. I knew I was on the right tracks... Thanks all.

How to find selected elements within a javascript marquee selection box without using a loop?

I am writing my own drag and drop file manager. This includes a javascript marquee selection box which when active calculates the elements (files) that are intersected and selects them by adding a class to them.
I currently perform the check during a mousemove handler, loop through an array of element coordinates and determine which ones are intersected by the drag and drop selection box.
The function currently looks like this:
selectItems : function(voidindex){
var self = this;
var coords = self.cache.selectioncoords;
for(var i=0, len = self.cache.items.length; i<len; i++){
var item = self.cache.items[i];
var itemcoords = item.box_pos;
if(coords.topleft.x < (itemcoords.x+201) && coords.topright.x > itemcoords.x && coords.topleft.y < (itemcoords.y+221) && coords.bottomleft.y > itemcoords.y){
if(!item.selected){
item.selected = true;
item.html.addClass('selected').removeClass('activebutton');
self.cache.selecteditems.push(i);
self.setInfo();
}
}
else{
if(item.selected){
item.selected = false;
if(!voidindex || voidindex !== i){
item.html.removeClass('selected');
}
var removeindex = self.cache.selecteditems.indexOf(i);
self.cache.selecteditems.splice(removeindex, 1);
self.setInfo();
}
}
}
},
There is lots of dirty logic in the code above which ensures that the DOM is only manipulated when the selection changes. This is not relevant to the question and can be exluded. The important part is the intersection logic which checks the coordinates of the element versus the coordinates of the marquee selection box.
Also please note that the item dimensions are fixed at 201px width by 221px height.
I have tested this and all works perfectly, however I have the need to support potentially thousands of files which would mean that at some point we will start seeing UI performance decrease.
I would like to know if there is anyway to perform intersection detection without looping through the coordinates of each element.
The coordinates of the marquee box are defined as follows at any given time:
selectioncoords : {
topleft : {
x : 0,
y : 0
},
topright : {
x : 0,
y : 0
},
bottomleft : {
x : 0,
y : 0
},
bottomright : {
x : 0,
y : 0
},
width : 0,
height : 0
}
And the coordinates of each item, stored in the self.cache.items array are defined as follows:
item : {
box_pos : {
x : 0,
y : 0
},
grid_pos : {
row : 1,
column : 1
}
}
So the information available will always be the actual grid position (row/column) as well as the physical item position (left and top offsets in pixels within the grid).
So to summarize, the question is, is there anyway to detect item intersection from a set of marquee selection box coordinates as defined above without looping through the whole array of item coordinates every time the mousemove event fires?
Thanks in advance for any help.
The following depends upon a locked grid with the dimensions as described.
You are comparing a mouse-defined rectangle against a grid with static edge sizes. Thus, given an x coordinate or a y coordinate, you should be able to derive pretty easily which column or row (respectively) the coordinate falls into.
When the user starts the select box, grab that x and y, and find the row/column of the start. When the mouse moves while pulling the select box, you find (and then update) the row/column of the finish. anything that is both within the rows defined by that box and within the columns defined by that box (inclusive) is selected. If you then keep your selectable elements in a two-dimensional array according to rows and columns, you should be able to just grab the ones you want that way.
Mind, how much more (or less) efficient this is depends on the size of your expected selection boxes as compared to the total size, and the degree to which you expect the grid to be populated. Certainly, if the average use case is selecting half or so of the objects at a time, there's not a whole lot you can do to cut down efficiently on the number of objects you have to look at each time.
Also, though it is kludgy, you can have the mousemove handler not fire every time. Letting it pause a bit between updates will reduce the responsiveness of this particular function a fair bit, but it'll cut down significantly on the amount of resources that are used.
There are several ways you could approach this. Here's one. First you need the items in some kind of organized structure that you can look up quickly by row and column. You could use a two-dimensional array, or for simplicity I'm going to use a hash table. You could do this at the same time that you create the self.cache.items, or later, something like this:
var cacheLookup = {};
function initCacheLookup() {
var items = self.cache.items;
for( var i = 0, n = items.length; i < n; i++ ) {
var item = items[i];
var key = [ item.grid_pos.row, item.grid_pos.column ].join(',');
cacheLookup[key] = item;
}
}
Then when you want to get the items intersecting the rectangle, you could do something like this:
var itemWidth = 201, itemHeight = 221;
var tl = selectioncoords.topleft, br = selectioncoords.bottomright;
var left = Math.floor( tl.x / itemWidth ) + 1;
var right = Math.floor( br.x / itemWidth ) + 1;
var top = Math.floor( tl.y / itemHeight ) + 1;
var bottom = Math.floor( br.y / itemHeight ) + 1;
var selecteditems = [];
for( var row = top; row <= bottom; row++ ) {
for( var col = left; col <= right; col++ ) {
var key = [ row, col ].join(',');
var item = cacheLookup[key];
if( item ) {
selecteditems.push( item );
}
}
}
// Now selecteditems has the items intersecting the rectangle
There's probably an off-by-one error or two here, but this should be close.
Well, as I said, that is one way to do it. And it has the possibly interesting property that it doesn't depend on the order of items in the self.cache.items array. But that cacheLookup hash table smells like it might not be the most efficient solution.
Let me take a guess: isn't that array already in the correct order by rows and columns (or vice versa)? For example, if your grid is four wide, then the top row would be array elements 0-3, the second row 4-7, the third row 8-11, etc. Or it could be a similar arrangement going down the columns.
Assuming it's in row-by-row order, then you don't need the hash table at all. That initCacheLookup() function goes away, and instead the search code looks like this:
var nCols = 4/*whatever*/; // defined somewhere else
var itemWidth = 201, itemHeight = 221;
var tl = selectioncoords.topleft, br = selectioncoords.bottomright;
var left = Math.floor( tl.x / itemWidth );
var right = Math.floor( br.x / itemWidth );
var top = Math.floor( tl.y / itemHeight ) * nCols;
var bottom = Math.floor( br.y / itemHeight ) * nCols;
var items = self.cache.items;
var selecteditems = [];
for( var iRow = top; iRow <= bottom; iRow += nCols ) {
for( var col = left; col <= right; col++ ) {
var index = iRow + col;
if( index < items.length ) {
selecteditems.push( items[index] );
}
}
}
// Now selecteditems has the items intersecting the rectangle
This code will be a little faster, and it's simpler too. Also it doesn't depend at all on the item.box_pos and item.grid_pos. You may not need those data fields at all, because they are easily calculated from the item index, grid column count, and item height and width.
Some related notes:
Don't hard code 201 and 221 in the code. Store those in variables once, only, and then use those variables when you need the item height and width.
There is a lot of duplication in your data structures. I recommend that you ruthlessly eliminate all duplicated data unless there is a specific need for it. Specifically:
selectioncoords: {
topleft: {
x: 0,
y: 0
},
topright: {
x: 0,
y: 0
},
bottomleft: {
x: 0,
y: 0
},
bottomright: {
x: 0,
y: 0
},
width: 0,
height: 0
}
More than half the data here is duplicated or can be calculated. This is all you need:
selectioncoords: {
left: 0,
right: 0,
top: 0,
bottom: 0
}
The reason I bring this up is that was a bit confusing when working on the code: "I want the left edge. Do I get that from topleft.x or bottomleft.x? Are they really the same like they seem? How do I pick?"
Also, as mentioned above, the item.box_pos and item.grid_pos may not be needed at all if the items are stored in a sequential array. If they are needed, you could store just one and calculate the other from it, since there's a direct relationship between the two:
box_pos.x === ( grid_pos.column - 1 ) * itemWidth
box_pos.y === ( grid_pos.row - 1 ) * itemHeight
You can limit the scope of your checks by indexing each item in a grid, as often as necessary and no more often. You can use the grid to give you a list of elements near an X, Y coordinate or that might be in an X1, Y2, X1, Y2 range.
To get you started ...
var Grid = function(pixelWidth, pixelHeight, boxSize) {
this.cellsIn = function(x1, y1, x2, y2) {
var rv = [];
for (var x = x1; x < x2; x += boxSize) {
for (var y = y1; y < y2; y += boxSize) {
var gx = Math.ceil(x/boxSize);
var gy = Math.ceil(y/boxSize);
rv.push(this.cells[gx][gy]);
}
}
return rv;
} // cellsIn()
this.add = function(x1, y1, x2, y2, o) {
var cells = this.cellsIn(x1, y1, x2, y2);
for (var i in cells) {
cells[i].push(o);
}
} // add()
this.get = function(x1, y1, x2, y2) {
var rv = [];
var rv_index = {};
var cells = this.cellsIn(x1, y1, x2, y2);
for (var i in cells) {
var cell = cells[i];
for (var oi in cell) {
if (!rv_index[cell[oi]]) {
rv_index[cell[oi]] = 1;
rv.push(cell[oi]);
}
}
}
return rv;
} // get()
this.cells = [];
for (var x = 0; x < Math.ceil(pixelWidth/boxSize); x++) {
this.cells[x] = [];
for (var y = 0; y < Math.ceil(pixelHeight/boxSize); y++) {
this.cells[x][y] = [];
}
}
};
So, rather than iterating through all possible objects, whatever they may be, you iterate over all the objects that are near or potentially in the given coordinates.
This requires that you maintain/re-index the grid as item coordinates change. And you'll likely want to add some functionality to the above (or similar) Grid class to modify/move existing objects. But, to the best of my knowledge, an index of this sort is the best, if not only, way to index objects "in space."
Disclaimer: The code above isn't tested. But, I have similar code that is. See the DemoGrid function class here: http://www.thepointless.com/js/ascii_monsters.js
The functionality of my DemoGrid is similar (as far as I remember, it's been awhile), but accepts x, y, radius as parameters instead. Also notable, my mouse events don't touch the grid every time the event fires. Checks are rate-limited by a game/main loop.
If the system is set up such that
self.cache.items is ordered from left to right and top to bottom
(0,0),(1,0),(2,0),(0,1),(1,1),(1,2),(0,2),(1,2),(2,2)
There is an item in each space
GOOD - (0,0),(1,0),(2,0),(0,1),(1,1),(1,2),(0,2),(1,2),(2,2)
BAD - (0,0),(2,0)(1,2),(1,3),(2,1),(2,3)
We need to know the total number of columns.
So the code to get you started.
// Some 'constants' we'll need.
number_of_columns = 4;
item_width = 201;
item_height = 221;
// First off, we are dealing with a grid system,
// so that means that if given the starting x and y of the marquee,
// we can determine which element in the cache to start where we begin.
top_left_selected_index = Math.floor(selectioncoords.topleft.x / item_width) + (Math.floor(selectioncoords.topright.y / item_height) * number_of_columns );
// Now, because the array is in order, and there are no empty cache points,
// we know that the lower bound of the selected items is `top_left_selected_index`
// so all we have to do is walk the array to grab the other selected.
number_columns_selected = (selectioncoords.bottomright.x - selectioncoords.topleft.x) / item_width;
// if it it doesn't divide exactly it means there is an extra column selected
if((selectioncoords.bottomright.x - selectioncoords.topleft.x) % item_width > 0){
number_columns_selected += 1;
}
// if it it doesn't divide exactly it means there is an extra column selected
number_rows_selected = (selectioncoords.bottomright.y - selectioncoords.topleft.y) / item_height;
if((selectioncoords.bottomright.y - selectioncoords.topleft.y) % item_height > 0){
number_rows_selected += 1;
}
// Outer loop handles the moving the pointer in terms of the row, so it
// increments by the number of columns.
// EX: Given my simple example array, To get from (1,0) to (1,1)
// requires an index increase of 3
for(i=0; i < number_rows_selected; i++){
// Inner loop marches through the the columns, so it is just one at a time.
// Added j < number_of_columns in case your marquee stretches well past your content
for(j=0; j < number_columns_selected && j < number_of_columns; j++){
// Do stuff to the selected items.
self.cache.items[top_left_selected_index + (i * number_of_columns) + j];
}
}

Uncaught TypeError: Cannot set property '0' of undefined "

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.

Categories

Resources