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
I'm given the task to deduce and explain the lines of codes of a counter program. Below are the codes to the program which is working perfectly. I have included my explanations as comments in the code but it seems my explanation for the last 4 lines of codes (starting from ... "if(counter[index][entry] === undefined){...}") doesn't really explain it.
Can anyone please read the codes and give me a better explanation to them especially why we equate "counter[index][entry] = 1".
<script>
//an array containing a list of objects with sub arrays that has to be
//counted "separately"
var arr = [
{"gateways":["ccu1"],"manufacturer":["homematic"],"ir":["ir_no"],"ip":
["ip_cam","ip_other"]},
{"gateways":["v3"],"manufacturer":["homematic"],"ir":["ir_no"],"ip":
["ip_cam"]},
{"gateways":["v2","v3","v4","ccu2"],"manufacturer":
["homematic","intertechno"],"ir":["ir_yes"],"ip":
["ip_cam","ip_other"]},
{"gateways":["v2","ccu1","ccu2"],"manufacturer":["homematic"],"ir":
["ir_yes"],"ip":["ip_cam","ip_other"]},
{"gateways":["gw_none"],"manufacturer":["homematic"],"ir":
["ir_no"],"ip":["ip_cam"]},
{"gateways":["v3","ccu2"],"manufacturer":
["homematic","fs20","intertechno","elro","Eltako Enocean"],"ir":
["ir_yes"],"ip":["ip_cam","ip_other"]},
{"gateways":["v3","v4"],"manufacturer":["homematic"],"ir":
["ir_no"],"ip":["ip_other"]},
{"gateways":["v3","v4"],"manufacturer":["homematic"],"ir":
["ir_no"],"ip":["ip_other"]},
{"gateways":["v2"],"manufacturer":["intertechno"],"ir":["ir_yes"],"ip":
["ip_other"]},
{"gateways":["v4"],"manufacturer":["homematic","intertechno"],"ir":
["ir_yes"],"ip":["ip_cam","ip_other"]}
];
//console.log(arr.length);
//first we create an empty array "counter" to contain the separately
//counted objects
var counter = [];
//we then use "for loop" to loop through the "arr" array to access the
//first index
for(var i=0; i<arr.length; i++) {
//console.log(arr[i]);
// we create a variable "obj" to store the first index object of
//the "arr" array and because it is a loop,
//it will loop till the last object
var obj = arr[i];
//so if we console log "obj", it will display the entire indexes in
//the "arr" array including keys
//console.log(obj);
//we then use "for in" loop to access all the keys in the variable
// "obj" because we wanna count the number
//of all respective sub arrays
for(var index in obj) {
//so if we console log "index", it will display the entire keys in
// the "obj" variable; ie:
//in every object, it will run 4X to access all the keys
//console.log(index);
//in the next two lines of codes, we have to check if the keys in
// our counter array already exist because
//this is where we gonna put or store our counted respective sub
//arrays. if it doesn't exit, we create it.
if(counter[index] === undefined) {
counter[index] = [];
}
//so if we console log "counter[index]", it will show empty
//arrays which is gonna contain the respective key counts
//console.log(counter[index]);
//next we save the respective arrays to be counted without the
// keys in a variable "arr2".
var arr2 = obj[index];
//console.log(arr2);
//now we wanna loop through the "arr2" array because it
//contains the entries (in arrays) we wanna count
///starting from the first index to the last
for(var j = 0; j < arr2.length; j++) {
//we then store the single entries in a variable called "entry"
//(not with keys or in an array)
var entry = arr2[j];
//console.log(entry);
//in the next two lines of codes, we have to check if the keys
//in our counter array exist because this is where
//we would count "entry"
if(counter[index][entry] === undefined) {
counter[index][entry] = 1;
//console.log(counter[index][entry]);
} else {
counter[index][entry]++;
}
}
}
}
console.log(counter);
</script>
If counter[index][entry] is undefined, it means that we haven't began counting it yet. So we need to start counting it. So we count once, which sets the counter to 1.
If counter[index][entry] does exist (the else clause), it means that there's already a number in counter[index][entry] (because that's what we initialize it to if it doesn't), so just increment the number by one.
Another, perhaps clearer way of writing this would be:
if (counter[index][entry] === undefined) {
counter[index][entry] = 0; // If it doesn't exist, initialize counter to 0.
}
counter[index][entry]++; // Here the counter is a number for sure, increment once.
Basically it's trying to count how many each specific property exists. Eg, how many occurences of gateway v2, how many times ip_other exists etc.
The part with 'if(counter[index] === undefined)' is weird. Counter is an array. And index is the key from an object (namely gateway, manufacturer, ip and ir) So basically it's trying to set keys on an array. Although this is valid javascript, counter should be an object instead of an array to be 'more logical'. Hence the code looks a bit confusing.
Anyways, the rest works like madara posted.
Title is pretty much self explanatory...
I want to be able to find duplicated values from JavaScript array.
The array keys can be duplicated so I need to validate only the array values.
Here is an example :
var arr=[
Ibanez: 'JoeSatriani',
Ibanez: 'SteveVai',
Fender: 'YngwieMalmsteen',
Fender: 'EricJohnson',
Gibson: 'EricJohnson',
Takamine: 'SteveVai'
];
In that example:
the key is the guitar brand
the value is the guitar player name.
So:
If there is duplicated keys (like: Ibanez or Fender) as on that current example that is OK :-)
But
If there is duplicated values (like: EricJohnson or SteveVai) I'm expecting to get (return) that error:
EricJohnson,SteveVai
You can't have associative arrays in Javascript. You can create an array of objects, like:
var arr=[
{Ibanez: 'JoeSatriani'},
{Ibanez: 'SteveVai'},
{Fender: 'YngwieMalmsteen'},
{Fender: 'EricJohnson'},
{Gibson: 'EricJohnson'},
{Takamine: 'SteveVai'}
];
Then you'll need a for...in loop to go over every object in the array, create a new array of values and check that for duplicates, which is also not very straightforward - basically you'll want to sort the array and make sure no value is the same as the one after it.
var arrayOfValues = [];
arr.forEach(function(obj){
for(var prop in obj)
arrayOfValues.push(obj[prop]);
});
arrayOfValues.sort(); // by default it will sort them alphabetically
arrayOfValues.forEach(function(element,index,array){
if(array[index+1] && element==array[index+1])
alert("Duplicate value found!");
});
First of all, object keys can not be repeated.
This means that:
({
"Fender": "Jim",
"Fender": "Bob"
})["Fender"]
Would simply return: "Bob".
However, I did make a code that could allow you to find duplicates in values, but as I said, the key will have to be unique:
var arr = {
Ibanez: 'EricJohnson',
Fender: 'YngwieMalmsteen',
Gibson: 'EricJohnson',
Takamine: 'SteveVai',
"Takamine2": 'SteveVai'
};
function contains(a, obj) {
for (var i = 0; i < a.length; i++) {
if (a[i] === obj) {
return true;
}
}
return false;
}
var track = [];
var exists = [];
for (var val in arr) {
if (contains(track, arr[val])) {
exists.push(arr[val]);
} else {
track.push(arr[val])
}
}
alert(exists)
You can see it working here: http://jsfiddle.net/dr09sga6/2/
As others have commented, the example array you provided isn't a valid JavaScript array. You could, however, keep a list for each guitar type:
var mapping = {
Ibanez: ['JoeSatriani','SteveVai'],
Fender: ['YngwieMalmsteen','EricJohnson']
Gibson: ['EricJohnson'],
Takamine: ['SteveVai']
];
Or a list of each guitar/musician pair:
var pairs = [
['Ibanez','JoeSatriani'],
['Ibanez','SteveVai'],
['Fender','YngwieMalmsteen'],
['Fender','EricJohnson'],
['Gibson','EricJohnson'],
['Takamine','SteveVai']
];
Your solution is going to depend on which pattern you go with. However, in the second case it can be done in one chained functional call:
pairs.map(function(e) {return e[1]}) // Discard the brand names
.sort() // Sort by artist
.reduce(function(p,c,i,a){
if (i>0 && a[i]==a[i-1] && !p.some(function(v) {return v == c;})) p.push(c);
return p;
},[]); //Return the artist names that are duplicated
http://jsfiddle.net/mkurqmqd/1/
To break that reduce call down a bit, here's the callback again:
function(p,c,i,a){
if (i>0
&& a[i]==a[i-1]
&& !p.some(function(v) {
return v == c;
}))
p.push(c);
return p;
}
reduce is going to call our callback for each element in the array, and it's going to pass the returned value for each call into the next call as the first parameter (p). It's useful for accumulating a list as you move across an array.
Because we're looking back at the previous item, we need to make sure we don't go out of bounds on item 0.
Then we're checking to see if this item matches the previous one in the (sorted) list.
Then we're checking (with Array.prototype.some()) whether the value we've found is ALREADY in our list of duplicates...to avoid having duplicate duplicates!
If all of those checks pass, we add the name to our list of duplicate values.
I've searched and found a few similar-but-not-quite answers.
I have an array SongList (showing 2 items for brevity...) - the first pair is a key, the second pair is some JSON.
SongList={
song_1:{title:"title of first song",artist:"the artist",mp3:"http://mysite/song1.mp3"},
song_2:{title:"title of second song",artist:"the artist",mp3:"http://mysite/song2mp3"}
...
};
I would like to be able to retrieve the key (song_1 or song_2) given the title in the value.
I will be looping through a temporary array of i items, each item in this array would have a match in SongList and I would save the key (song_1, song_2) in a final array.
You don't have an array, you have an object, containing more objects. Use for in
function findTitle(title) {
for (var key in SongList) {
if (SongList[key].title == title) return key;
}
return false;
}
And then call it!
findTitle("title of first song"); //returns "song_1"
findTitle("BNOT MEEEEEE"); //returns false
Here is an example.
var one = {
a: {b:"MightyMouse", d:2},
b: {b:"MickeyMouse", d:4},
c: {b:"Superman", d:6}
};
for (outerkey in one) {
if (one[outerkey].b === "Superman") {
console.log ("Outerkey = " + outerkey);
}
}
Assuming you are looking for Superman, this prints as expected c.
Thanks everyone, I realize my understanding of Arrays v. Objects was an issue which obviously hindered my Google-Fu. I hope it's ok to post the answer I finally arrived at via guidance here:
(SongList object is described in question above)
This is the eventual function I arrived at for saving the keys of the playlist SongList:
$("#save_playlist_as_keys").click(function() {
var keys = []
for(var i=0; i<myPlaylist.playlist.length; i++){
var playItem = (myPlaylist.playlist[i].title); //this returns the names of the tracks
for (var k in SongList){
if(SongList[k].title == playItem) keys.push(k);//this matches track name to keys
}
}
localStorage.setItem('storedKeys',keys);
});
This seems to be doing what I want for now.
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.