I am trying to pass some data to a function that uses those arguments as identifiers for a multi dimensional array, and then return the value hardcoded to that array. I am not sure what I am doing wrong, but something is breaking.
I can get an alert() to pop before I assign any array values, but it seems to die at that point. Any help is appreciated.
// Get Column A's number
var a1 = Number($('#p-a').attr("numb"));
// Get Column B's number
var b1 = Number($('#p-b').attr("numb"));
// Get status for column A
var a_status = $('#p-a').attr("status");
// Get status for column A
var b_status = $('#p-b').attr("status");
// If same, status="s" else, status="i"
var status = "";
if(a_status == b_status) { status = "s"; }else{ status = "o"; }
// Get the value of the numbers + status
var a = this_function(a1, b1, status, "2");
// Update the status div
$('#status').html(a);
function this_function(a1, a2, s, p)
{
this_array = array();
this_array['1']['1']['1']['1'] = "10";
this_array['1']['1']['1']['2'] = "20";
this_array['1']['2']['1']['1'] = "40";
this_array['1']['2']['1']['2'] = "60";
//
return this_array[a1][a2][s][p];
}
You cannot initialize arrays like that. Every level needs to be initialized individually. And as you don't have numerical keys only, I'd use an object instead:
var this_array = {
'1': {
'1': {
'o': {
'1': "10",
'2': "20"
}
},
'2': {
'o': {
'1': "40",
'2': "60"
}
}
}
};
You'd also have to define what happens if a key does not exist. E.g. currently, if status is 's' then you will get an error.
The if statement can be written shorter using the conditional operator:
var status = (a_status == b_status) ? 's' : 'o';
Update: If you really want to have a numerical array, provided the keys are numerical only, you can create the array like so:
var this_array = [
[], // this_array[0]
[ // this_array[1]
[], // this_array[1][0]
[ // this_array[1][1]
[], // this_array[1][1][0]
[null, 10, 20] // this_array[1][1][1][...]
],
[ // this_array[1][2]
[], // this_array[1][2][0]
[null, 40, 60] // this_array[1][2][1][...]
]
]
];
You see, if you do not start your indices with 0 the structure becomes quite confusing.
Your array notation within this_function is incorrect (barring your having an array function that creates the array in the form you show). Notes:
function this_function(a1, a2, s, p)
{
this_array = array(); // <== There is no `array` function in std. JavaScript
this_array['1']['1']['o']['1'] = "10"; // <== Unless you've created an object/array at this_array['1'] (which you haven't), this line will fail
this_array['1']['1']['o']['2'] = "20";
this_array['1']['2']['o']['1'] = "40";
this_array['1']['2']['o']['2'] = "60";
//
return this_array[a1][a2][s][p];
}
I'm not entirely sure what this_function should do, or I'd offer a replacement function. Some thoughts:
Creating an array, you use [] (or new Array(), but that's just a longer form of the same thing).
You have to create each object/array in an array. So you can't assign to this_array['1']['1']['o']['1'], for instance, until you've created an object/array at this_array, this_array['1'], this_array['1']['1'], and this_array['1']['1']['o'].
Your this_function function will create a new array each time it's called. That seems dramatically inefficient.
JavaScript arrays aren't really arrays, they're just objects with some special features. You may just want objects, given that not all of your keys are numeric. If you really want arrays, though, they mostly start with index 0 rather than 1.
You're quite correct that array indexes are really strings, but they're almost always written as numbers and it's totally fine to do that (leaving off the quotes). Using the quotes (which is, again, technically correct) will tend to confuse people trying to maintain the code. (But if you use objects rather than arrays, it will mostly help.)
First and foremost there is no array() function in Javascript. I don't know if it refers to some other point of your code but arrays are created via the array constructor new Array() or an array literal []
Secondly what you are using is not a real numberical indexed array.
For the assignment part: you have one array/object but the deeply nested objects/arrays are undefined.
Your code dies at: this_array['1']['1']['o']['1'] = "10"; because this_array['1'] is undefined which can't have any property so the chain is broken.
Then there is a problem with types. You convert the attribute to number by Number($('#p-a').attr("numb")); and then you use strings as indexes. This is related to the array/object confusion.
What you need is to create a real array, and use numerical indexes:
// move it outside so you only
// create this beast once
var array = [ // first level
[ // second level
[ // third level
[10, 20] // fourth level
],
[
[40, 60]
]
// , [...]
]
];
function this_function(a1, a2, s, p) {
return array[a1-1][a2-1][s-1][p-1];
}
i'm not well versed in needing to deal with multidimensional arrays, but you need to define all your inner arrays before you can set them to anything. something like this:
var this_array = [];
this_array['1'] = [];
this_array['1']['1'] = [];
this_array['1']['2'] = [];
this_array['1']['1']['o'] = [];
this_array['1']['2']['o'] = [];
this_array['1']['1']['o']['1'] = "10";
this_array['1']['1']['o']['2'] = "20";
this_array['1']['2']['o']['1'] = "40";
this_array['1']['2']['o']['2'] = "60";
i tried to console this result out and everything came up as undefined, but within the array at least and didn't die.
Related
Here is my code:
var arr = [[[[[]]]]];
var c = 20;
for (i=0;i<5;i++)
arr[i][0][0][0] = c;
alert(arr[2][0][0][0]);
It doesn't work, but how can I do this?
Most people here are using for loops, which I think are mostly obsolete in the age of anonymous functions in JavaScript. You people should know better :P
Anyway, you can solve this quite nicely in a one-liner. Here are a few scripts that can initialize your array...
If you already have a 4-dimensional array, you can initialize it elegantly like this:
arr.forEach(function(e) { e[0][0][0] = c })
Or, if you're more into map:
arr.map(function(e) { e[0][0][0] = c })
These are assuming you already have c defined, which you do in your code sample (20).
From now on, though, please Google your questions before asking them on stackoverflow. You will receive an answer that has already been accepted :)
It doesn't work because you haven't specified any elements beyond the first one, so the length of array is one and accessing further keys is incorrect.
I think, the most convenient way would be to push a new 3d array with c inside on every iteration (actually I have no idea what you're trying to achieve with this xD):
var arr = [];
var c = 20;
for (i=0;i<5;i++)
arr.push([[[c]]])
alert(arr[2][0][0][0]);
(in your example it's actually 5d, but as you've asked for 4d, writing 4d there)
It is unclear what you want, but I imagine a 4 dimension array is an array that has a set of arrays nested 3 deep, each of which has an array nested 2 deep, each of which has a single array that contains values.
In a one dimension array, you access the value at index 2 by:
arr[2];
In a two dimension array, you'd access the value at (2,3) by:
arr[2][3]
and so on until you get to the value at (2,3,1,2) in a four dimension array by:
arr[2][3][1][2]
and if that was the only value in the array, it would look like:
[,,[,,,[,[,,'value at 2312']]]];
If there was also a value at (1,1,0,2) the array would now look like:
[,[,[[,,'value at 1102']]],[,,,[,[,,'value at 2312']]]];
There can only be values in the last nested array, the value at indexes in every other array must be another array (for the lower dimensions), so to insert at value at, say (2,1,3,1) and assign it a value of 6, you need to loop over the array and inspect each index. If it's not already an array, insert an array and keep going, e.g.:
// Insert value in arrary at coord
// coord is a comma separated list of coordinates.
function insertValue( array, coord, value) {
var coords = coord.split(',');
var arr = array;
for (var c, i=0, iLen=coords.length-1; i < iLen; i++) {
c = coords[i];
if (!Array.isArray(arr[c])) arr[c] = [];
arr = arr[c];
}
arr[coords[i]] = value;
return array;
}
document.write('result: ' + JSON.stringify(insertValue([],'1,2,1,3','at 1213')));
I don't understand what you are trying to do in the OP: are you trying to create a value of 20 at coordinates (0,0,0,0), (1,0,0,0), (2,0,0,0), etc.? If that is the case, you also need a fill function that will iterate for the required number of times and pass suitable arguments to insertValue.
If that's what you want, then given the above you should be able to write such a function. On the first iteration it would pass:
insertValue(array, '0,0,0,0', 20)
and on the second:
insertValue(array, '1,0,0,0', 20)
and so on. You may wish to modify the function so that instead of the coords being a CSV string, you pass an array like [0,0,0,0] (which is what split turns the CSV string into), but that's up to you.
Note that you must pass all 4 dimensions, otherwise you will replace one of the dimension arrays with a value and effectively delete all other points in that dimension sector.
PS
ES5 introduced forEach, which helps encapsulate loops but doesn't necessarily mean less code, or faster execution, than an equivalent for loop:
// Insert value in arr at coord
// coord is a comma separated list of coordinates.
function insertValue( array, coord, value) {
var arr = array;
var coords = coord.split(',');
var last = coords.pop();
coords.forEach(function(c) {
if (!Array.isArray(arr[c])) arr[c] = [];
arr = arr[c];
})
arr[last] = value;
return array;
}
Create array with 5 nested arrays:
var arr = [[[[[]]]], [[[[]]]], [[[[]]]], [[[[]]]], [[[[]]]], [[[[]]]]];
var c = 20;
for (i=0;i<5;i++)
arr[i][0][0][0] = c;
alert(arr[2][0][0][0]);
EDIT: if you dig into functional programming and recursion, you can initialize your multidimensional array with just a few lines of code. Let's say you want 4-dimensional array with length 10 of each dimension:
function createNDimensionalArray(n, length) {
return n === 1
? new Array(length)
: Array.apply(null, Array(length)).map(createNDimensionalArray.bind(null, n - 1, length));
}
var arr = createNDimensionalArray(4, 10);
console.log(arr); // creates 4-dimensional array 10x10x10x10
Notice that initialization like this could be very slow if you create very big arrays (e.g. createNDimensionalArray(5, 10000).
If you prefer to set length of each dimension, you can modify previous the solution like this:
function createNDimensionalArray(dims) {
return dims.length === 1
? new Array(dims[0])
: Array.apply(null, Array(dims[0])).map(createNDimensionalArray.bind(null, dims.slice(1)));
}
var arr = createNDimensionalArray([2, 3, 4, 5]);
console.log(arr); // creates 4-dimensional array 2x3x4x5
not sure how to ask tbh :)
I'm used of PHP's associative arrays so much that I struggle to understand how to create an "named array" of objects.
Example:
I have two arrays, two ints and one boolean. This represents one of my entities. I have multiple entities on which I'm doing some work.
In PHP I would write:
$entitites[$entitity_id]['items'][] = $item;
$entitites[$entitity_id]['items_status'][] = $item_status;
$entitites[$entitity_id]['items_count']++;
and so on..
How do I do this with objects in JS?
var entities = {items:[], items_status: [], items_count: 0};
entities[entity_id].items.push(item)
How does one name his object for later access (via name or in my case, entity_id?)
This code doesnt work for me to this extend that my webpage goes blank without any errors produced :S
I also tried this:
var entities = {};
var entity = {items:[], items_status: [], items_count: 0};
but then I dont know how to always add values to already existing object in entities object and how to call that exact object via name eg. entity_id.
Halp :(
Keep entities as an object. Then you can just go ahead and add each entity_id as a key and an object which has all the details of that entity as the value.
var entities = {};
entities["1234"] = {
"items" : [],
"items_status" : [],
"items_count" : 0
};
There are 2 types involved here: Objects & Arrays.
Arrays are simple and you're probably familiar with them from any other language:
var myArray = []; // this is an empty array
myArray[0] = 1;
myArray[1] = 2;
myArray[2] = 3;
// you could also use "var myArray = [1, 2, 3];" instead
alert(myArray[1]); // alerts the value 2
Note: arrays are actually objects, and can have non-index properties as well
You can also use various array functions such as .push(), .pop(), .shift() and so on to mutate the array instead.
Objects share the square brackets notation, but the purpose is different:
var myObject = {}; // this is an empty object
myObject[0] = 1;
myObject[1] = 2;
myObject[2] = 3;
alert(myObject[1]); // alerts the value 2
// but also...
myObject['prop'] = 4;
alert(myObject['prop']); // alerts the value 4
// and
myObject.prop2 = 5;
alert(myObject.prop2); // alerts the value 5
// and lastly
alert(myObject.prop); // alerts the value 4
So while arrays are accessed by index, objects are accessed by property names.
As for your entities, it looks like an array of objects. Lets see how we can do that:
function Entity() {
this.items = [];
this.items_status = [];
this.items_count = 0;
}
var entitites = [];
entities.push(new Entity());
entities[0].items = [1, 2, 3];
entities[0].items_status = ['good', 'good', 'poor'];
entities[0].items_count = 3;
Or you can wrap insertion in a more elegant function:
Entity.prototype.insert(item, status) {
this.items.push(item);
this.items_status.push(status);
this.items_count++;
}
entities[0].insert(4, 'excellent!');
If you want to keep control of the indexes in your JS array you can do so by not using .push() :
var entities = [];
entities[5] = {items:[], items_status:[], items_count:0};
Just replace 5 by your integer entity_id variable, and there you go.
You can use a regular javascript object to create the associative array you're looking for.
Actually it's PHP's implementation that's abit off but all they do is call it different (associative array) to most other language that simply refer to it as an object or hash.
You can use numeric keys in JS and still access them with the [] square brackets.
It works like this:
var my_obj = {};
my_obj[5] = 'any value';
console.log(my_obj); // {5: 'any value'}
JS will not add any redundant undefined to missing indexes either so when looping over the collection you won't loop over undefined.
Also, I can access the object by using the key as a string or as number so you won't have to check if the key is the right type. Taken from the above example:
console.log(my_obj['5']); // 'any value'
console.log(my_obj[5]); // 'any value'
JS Objects are the equivelant of PHP assoc arrays except JS objects are much more flexible than PHP's associative arrays.
The only downside to this is that you can't have duplicate keys.
No two keys may exist that share the same name, in an array if you .push(an_item) it will create a new index making even a duplicate data entry unique but when overwriting a key with a new value only the last value will persist, mind that :)
I have the following requirement. I have a pair of integers which is to act as keys and another pair of integers which should act as values. That is:
obj[{key1:12,key2:23}]=[2,3];
obj[{key1:12,key2:22}]=[4,3];
obj[{key1:34,key2:12}]=[4,33];
Also finally when the population of this list is over, I would like to sequentially access the elements of the object/array.
Now it is my understanding that for such arrays which take an object as key, they are known as associative arrays and Javascript doesn't support them..
The following will be the operations I will perform on this structure :
Insertion: I will have keys like (2,3) or (2,4) which I would like to insert into the array with a new keyvalue pair such as [1,2],
Lookup : I may have a key pair like (2,3) which is already inserted into this array and I would like to get it back so that I can modify it.
That is something like:
if(obj[{key1:2,key2:3}])
obj[{key1:2,key2:3}]=[2,5];
else
obj[{key1:2,key2:3}]=[2,-1];
Any suggestions as to how I can implement this in Javascript?
EDIT: These are the two things I tried:
First I made it as an array of objects. This approach didn't work because from looking around, I knew that in such cases, Javascript will call the toString method to get the string equivalent of the object which it will then use an index.
Second, I tried to do an object with object keys containing sub-objects. Something along the lines of this answer: Answer. However I am not sure how to get sequential access to all the elements after I am done with the insertion phase.
You're probably not going to like this much, but it'll at least give you a stable key:
obj[JSON.stringify({key1:12,key2:23})]=[2,3];
So, the big problem is that in an object the 'key' (really, the 'property') must be a string, or be able to be stringified. In your examples, {key1:12,key2:23} will always be stringified to [object Object]. So you'll never get a unique key. The only way to get that unique key is to really serialize it, such as by using the JSON.stringify method.
Note that on IE8 I think you have to include a JSON class.
Here is an object oriented way to do it:
// Constructor
function AssociativeArray() {
this.length = 0;
}
// Add or set value
AssociativeArray.prototype.set = function(key, value) {
key = key.key1+'|'+key.key2;
if(!this[key]) {
this.length++;
}
this[key] = value;
};
// Lookup
AssociativeArray.prototype.get = function(key) {
return this[key.key1+'|'+key.key2];
};
AssociativeArray.prototype.toString = function() {
var k, arr = [];
for(k in this) {
if(this.hasOwnProperty(k) && k !== 'length') {
arr.push(this[k]);
}
}
return arr;
};
// Create Associative Array
var arr = new AssociativeArray();
// empty array
console.log(arr.toString(), 'length='+arr.length); // [] length=0
// add value
arr.set({key1:1, key2:2}, [1,1]);
console.log(arr.toString(), 'length='+arr.length); // [[1,1]] length=1
// add value
arr.set({key1:2, key2:1}, [2,2]);
console.log(arr.toString(), 'length='+arr.length); // [[1,1], [2,2]] length=2
// set value
arr.set({key1:2, key2:1}, [3,3]);
console.log(arr.toString(), 'length='+arr.length); // [[1,1], [3,3]] length=2
// lookup and set
if(arr.get({key1:2, key2:3})) {
arr.set({key1:2, key2:3}, [2,5]);
} else {
arr.set({key1:2, key2:3}, [2,-1]);
}
console.log(arr.toString(), 'length='+arr.length); // [[1, 1], [3, 3], [2, -1]] length=3
Fiddle here: http://jsbin.com/ohOwala/3/edit
You could use a bidimensional array
var arr = [];
arr[2] = [];
arr[2][3] = [1, 2];
Or you could use an object and access the pairs using the object properties names
obj = {
_2_3: [1, 2],
_2_1: [4, 1],
_1_2: [3, 2]
};
and access them like this obj["_2_3"] or this obj._2_3
or maybe you could nest em
obj = {
_1: {
_2: [2,1]
}
};
so you could access them like this obj["_1"]["_2"]
or maybe this
obj = {
1: {
2: [2,1]
}
};
But you will be forced to use associatve array notation obj["1"]["2"]
and as far as i know using the associative array like way for accessing objects properties isnt a good practice
I asked where the objects {key1:2,key3:2} came from because if you have control over it you can implement a toString method for those types that will take care of the Object to string conversion so it can be used as a property name.
//define keypair object type
var MyKeyPair = function(key1,key2){
this.key1=key1;
this.key2=key2;
};
//define tostring for this type
// later obj[aKeyPairInstance] will
// invoke the toString method
// if you don't do this then [Object object]
// would be returned for toString
MyKeyPair.prototype.toString=function(){
//since you know there is only going to be key1 and key2
// you could just:
// return this.key1+":"+this.key2;
//Here follows a more general approach but it'll cost
// you more cpu time, if working with very large amounts
// of data use the shorter version.
var ret=[];
for(thing in this){
if(this.hasOwnProperty(thing)){
ret.push(thing);
ret.push(":");
ret.push(this[thing]);
ret.push(",");
}
}
return ret.join("");
};
// make a bunch of keyPair objects
var keys = [
new MyKeyPair(21,33),
new MyKeyPair(22,34),
new MyKeyPair(23,35),
new MyKeyPair(24,36)
];
//create an object and give it properties
// based on the tostring value of the keypairs
var obj={};
for(var i = 0,len=keys.length;i<len;i++){
obj[keys[i]]=[keys[i].key1,keys[i].key2];
};
console.log(obj);//<=this would not log any usefull info in IE
//Use Chrome, Firefox, Opera or any other browser instead
I am trying to set up an array in jQuery and I then need to do a for loop on it. But it seems that I cant use an associative array for some reason?
var items = new Array();
items['foo'] = 123456;
items['bar'] = 789012;
items['baz'] = 345678;
items['bat'] = 901234;
alert(items.length);
This is just a test, but it return 0?
You can't make associative array in JavaScript like what you want, instead you can use Object.
For example:
var items = {
foo : 123456,
bar : 789012,
baz : 345678,
bat : 901234
}
And to calculate the length you can do:
var getObjectSize = function(obj) {
var len = 0, key;
for (key in obj) {
if (obj.hasOwnProperty(key)) len++;
}
return len;
};
Use: getObjectSize(items); // output: 4
For more see here.
Another one is:
Object.keys(items).length;
But not supported by all browsers.
var items = new Array();
items['foo'] = 123456;
The problem lies in the very first line. You believe that you are adding an item to the array at the index foo, but you are actually adding a property to the items variable with a key foo and value 123456. If you were to type items.foo it would give you back your 123456.
The problem with this approach is that adding a property to an array does not magically increase it's length.
If you want to have non-numeric indexes, you need to use an object instead of an array:
var items = {
foo: 123456,
bar: 789012,
baz: 345678,
bat: 901234
};
Another approach might be to set up two different arrays, which you construct in parallel:
var items = [], items2 = [];
items.push('foo');
items2.push(123456);
// etc.
alert(items2.length);
The efficiency of this approach depends on how you'll use it. If you're only going to loop through the list of items and do something to each of them, this approach may be more efficient. But if you need to use it like an associative array (items['foo']), then you're better off building an object.
The .length property returns the highest numerical index of the array. Thus, in your case, there is no numerical index and it returns 0. Try
items[98] = "something";
items.length will be 98..! Use the .length property with caution, and if you also want to count the non-numerical indici, loop over the Object (an Array is also an Object) and count its ownProperties.
I need to work through a source array of objects, many of the objects in the array have three property values which will be the same. It is these values that will be used to create a new object and push it on to destination array. When another object on the source array comes up with the same three property values as one of the objects on the destination array the matching object on the destination array will have its visit count incremented by one.
To help you understand, in the source array each object is a record of a meal that belongs to a user. In the second array I need to store the user details and the number of their meals.
I've tried a few solutions which have failed like the one below. I thought that the code below would create a literal object, check if it is in the destination array by finding it's indexOf (-1 for not found) and if it's not found push it on. The problem is that it never finds the objects, if I search through 3000 meals the second array ends up 3000 long!
The code below does not try to store the visit count.
userArray = new Array();
for (var i = 0; i < filteredObjects.length; i++) {
var user = {
forname: filteredObjects[i].forname,
surname: filteredObjects[i].surname,
dincat: filteredObjects[i].dincat,
};
var index = userArray.indexOf(user);
if (index = -1) {
userArray.push(user);
}
}
This doesn't work because the user object that you create in the loop is not the same as any of the objects you added inside userArray. They might contain the same keys and values, but strictly speaking (===) they're not the same.
To help your code, you can add a user map object:
var userArray = new Array(),
userMap = {};
for (var i = 0, item; item = filteredObjects[i]; ++i) {
var userKey = item.forname + '-' + item.surname + '-' + item.dincat;
if (!(userKey in userMap)) {
userArray.push({
forname: filteredObjects[i].forname,
surname: filteredObjects[i].surname,
dincat: filteredObjects[i].dincat,
});
userMap[userKey] = true;
}
}
The user map is an object that uses its keys to determine whether you have already inserted a user before. This works by choosing a user identifier, in your case the combination of first name, surname and dincat.
indexOf compares search element to elements of the Array using strict equality. If both operands are objects, they're compared as objects, and the equality test is true only if both refer the same object. In your case, you create a new object every time and compare it with the existing objects in the array and it will return false.
There's several syntax errors there, but the major reason that code's not working is that what you're doing is creating a new object with the value of the object you currently at in the array loop, then looking for that new object in the array, so it's never going to be there.
I'm actually a little curious myself if there's a more efficient solution, but one possibility is
var demo = [
{a: 'green', b: 'blue', c:'red'},
{a: 'blue', b: 'green', c: 'not blue'},
{a: 'green', b: 'blue', c: 'red'}
],
records= {};
for (var i=0; i<demo.length; i++){
if (records.hasOwnProperty(demo[i].a) &&
records[demo[i].a].hasOwnProperty(demo[i].b) &&
records[demo[i].a][demo[i].b].hasOwnProperty(demo[i].c)
){
//do something with a match
} else {
if (!records.hasOwnProperty(demo[i].a))
records[demo[i].a] = {};
if (!records[demo[i].a].hasOwnProperty(demo[i].b))
records[demo[i].a][demo[i].b] = {};
records[demo[i].a][demo[i].b][demo[i].c] = 'yes';
//no match found
}
}
Just substitute your values in for a, b, and c and it should work.