for( … in …) not working with array - javascript

I'm somehow confused:
I have a list of commands like this:
var commands = [{"command": "read"}, {"command": "write"}, {"command": "login"}];
If I try it access one of the commands like this it works:
console.log(commands[0]["command"]); // Output is "read"
console.log(commands[0].command); // Output is "read"
But if I try this the output is always undefined:
for(command in commands)
console.log(command["command"]); // undefined, undefined, undefined

for does an array iteration in javascript, so you want:
for(command in commands)
console.log(commands[command]["command"]);
ie, the command variable in your example is an array index, not the enumerated item from the array.

The for ... in construct iterates over the keys of the objects in the array, not the objects themselves. So you would need to write:
for(index in commands)
console.log(commands[index]["command"]);

The for (.. in ..) construct is for looping over objects, not arrays. Since you have an array of objects, you should be doing:
for (var i = 0, j = commands.length; i < j; i += 1) {
console.log(commands[i].command);
}
For a thorough explanation as to why you should use this for construct instead of the for...in, see answer #3010848.

Why use for..in with an array? Just access by index, and you also avoid potential problems of prototype extensions (see hasOwnProperty)
var i,len=commands.length;
for (i=0;i<len;i++ ) {
console.log commands[i].command
}
If order does not matter, more concisely
for (i=commands.length-1;i>=0;i-- ) {
}
Or
var i=commands.length;
while (i--) {
...
}

Use it like this
for(var x in commands)
console.log(commands[x].command);

Have you tried:
for(command in commands[0]) {
console.log(command["command"]);
}

Related

How to completely remove an empty array in javascript?

I have searched and searched and cannot find an answer to this, so please accept my apologies if I am being foolish.
I am building an application in node.js that uses JSON files as project configs. I am using the 'fs' module to read the JSON file, which I then parse into a Javascript object. I then amend details within the object before using JSON.stringify to write the data back to the file.
My problem is that I need to delete an array, not just empty it, in order to show the data correctly on my interface:
"floors": [
{
"floorName": "Grd Floor",
"floorNotes": "",
"floorPlan": "Buckley_Grd.jpg",
"floorWidth": "57.392",
"floorHeight": "20.776",
"runs": [
[], *<----I cannot delete these*
[] *<----only empty them*
[
{
"xCoOrd": "37.88235294117647",
"yCoOrd": "59.307359307359306",
"drawingType": "node"
},
{
"xCoOrd": "37.88235294117647",
"yCoOrd": "59.307359307359306",
"drawingType": "node"
},
{
"xCoOrd": "48.549019607843135",
"yCoOrd": "50",
"drawingType": "node"
}
] *<----I don't want to delete the elements in this array*
]
}
I have tried:
.splice(0);
.filter(Boolean);
delete
Everywhere I have looked people seem surprised that one would want to delete an unused array from memory, which is what I beleive I am trying to do. Perhaps there is a way to stringify the object that ignores these empty arrays? Or perhaps I am doing this completely the wrong way?
Many thanks in advance for any advice offered.
EDIT: I should have made clearer that I don't necessarily want to delete all of the elemnets in the array so redeclaring floors.runs =[] unfortunately will not help me.
I have tried:
delete floors[floorID].runs[runID];
This replaces the array with null which gets written to the file and ruins my reading of the JSON.
Did you try over-writing the 'runs' property of each object? If you said floors[0].runs = [] (or floors[i].runs = [], if you're looping through), then those two empty arrays would no longer show up in your stringify.
Editing my answer, to match the OP edits:
floors.forEach(function(floor){
var newRuns = [];
floor.runs.forEach(function(run){
if (run.length > 0){
newRuns.push(run);
}
})
floor.runs = newRuns;
})
After I assign your object from the OP into a variable and run my code on it, then stringify the result, I get this as a return:
{"floors":[{"floorName":"Grd Floor","floorNotes":"","floorPlan":"Buckley_Grd.jpg","floorWidth":"57.392","floorHeight":"20.776","runs":[[{"xCoOrd":"37.88235294117647","yCoOrd":"59.307359307359306","drawingType":"node"},{"xCoOrd":"37.88235294117647","yCoOrd":"59.307359307359306","drawingType":"node"},{"xCoOrd":"48.549019607843135","yCoOrd":"50","drawingType":"node"}]]}]}
Is that what you're hoping to see? I used this fiddle to produce that result.
If you want remove ALL values from runs array you simply do this:
floors[i].runs = null
If you want remove only the empties
var filled = [];
obj.floors[i].runs.forEach(function(value, i, array) {
if (value.length != 0) filled.push(value);
});
obj.floors[i].runs = filled;
Runs is a property of an object, you can remove items from that array. In your example above floors[0].runs.splice(0,2), should remove both of those arrays I think.
Keeping the same reference
Iterating downwards lets you safely perform operations which decrease the length of an Array by 1 each time
var i, parentArray = obj['floors'][0]['runs'];
for (i = parentArray.length; i >= 0; --i) {
if (parentArray[i].length === 0) parentArray.splice(i, 1);
}
Making a new reference
Creating a new array without the values you don't want can be achieved using .filter
obj['floors'][0]['runs'] = obj['floors'][0]['runs'].filter(function (x) {
return x.length > 0;
});
To remove an object completely you have to remove the reference from the parent. To do this what you can do is create a new Array in which you copy only the references to the non-empty arrays.
I have created this function which should remove all empty arrays. I haven't tested it too much, but this should give you an idea:
function clearEmptyArrays(obj) {
for(var key in obj) {
var arr = obj[key];
if(Array.isArray(arr)) {
// Iterate through all elements and remove empty arrays
var nonEmptyArr = [];
for(var i = 0; i < arr.length; ++i) {
// Recursive call if it's an object inside an array
if(typeof arr[i] === 'object') clearEmptyArrays(arr);
// Save all non-empty arrays or objects inside this one
if((Array.isArray(arr[i]) && arr[i].length > 0) || !Array.isArray(arr[i])) {
nonEmptyArr.push(arr[i]);
}
}
obj[key] = nonEmptyArr;
return;
}
// Recursive call if it's an object
if(typeof arr === 'object') {
clearEmptyArrays(arr);
}
}
}
You can call it like this: clearEmptyArrays(myObj); and will update the Object in-place.
Demo: https://jsfiddle.net/zeo7dauh/

Iterating JavaScript object with strings as keys

I am building a JavaScript array, which has strings as keys.
The array should have an object at every entry. My object looks like this (a console.log of my variable rIds):
Now, the length of this object is 0, which makes it impossible to iterate it.
I want to iterate every key, so I can retrieve and access my list of ids (rIds[key].productIds).
Code:
var rIds = [];
var response = RR.data.JSON.placements;
console.log(response);
$(response).each(function (placementId) {
var placement_name = this.placement_name;
rIds[this.placement_name] = {
productIds: []
};
$(this.recs).each(function (recIndex) {
var pid = this.pid;
pid = getRandomId(19341610, 19341746);
rIds[placement_name].productIds[recIndex] = pid;
});
});
var count = '<%=ViewBag.Get("RecommendationCount") %>';
console.log(rIds);
console.log(rIds.length);
$.each(rIds, function (index, val) {
console.log(val); // This should work as expected.
});
What did I try?
I found the following snippet, which does not work in IE8. However, this does not really help be, even though Object.keys(rIds).length results in the correct length.
for (var index = 0; index < Object.keys(rIds).length; index++) {
console.log(rIds[index]);
}
However, this code does not make my access the object.
Tl;dr & question
How do I iterate my JavaScript object which has strings as keys, so I can access the individual entries?
The Object.keys() function returns an array containing the property names of the object.
var keys = Object.keys(rIds);
for (var i = 0; i < keys.length; ++i) {
console.log(rids[keys[i]]); // object value
}
Alternatively, you can use the .forEach() method:
Object.keys(rIds).forEach(function(key) {
console.log(this[key]);
}, rIds);
Finally there's the venerable for ... in loop:
for (var key in rIds) {
if (rIds.hasOwnProperty(key))
console.log(rIds[key]);
To support versions of IE before IE9 you're kind-of stuck with for ... in, though you can find mostly-correct "polyfills" for Object.keys() and Array.prototype.forEach() at MDN.
To supplement Pointy's answer, you can also use jQuery's $.each (since you're already using jQuery elsewhere) to iterate over each key/value pair in your rIds object:
$.each(rIds, function(key, value) {
console.log(value);
});

JavaScript - Array behaves like object

I'd like to ask a question about JavaScript arrays.
Does the array behave like an object when we use for..in loop to iterate. I mean in this case can indexes take the role of properties(keys).
While you can do for..in syntax on an array, you shouldn't because you will will iterate over any properties you may have assigned to the array.
Example:
var array = [0, 1];
array.three = 2;
for (var p in array){
console.log(array[p]); //print 0, 1, 2
}
for(var i = 0; i < array.length; i++){
console.log(array[i]); //prints 0, 1
}
So, when dealing with arrays you should always use the for var i approach to avoid running into unexpected behavior.
As everything in JS, an array is an object.
It means you can use an array as a prototype :
var obj = Object.create([]);
console.log(obj instanceof Array); // true
obj[0] = "value 1";
obj.test = "value of property test";
for(var i in obj) console.log(obj[i]); // "value 1" "value of property test"
or any other thing you would do with an object, including using a for ... in loop.
However, the length property is updated with the highest (integer) index of the array +1.
var arr = ["one","two"];
arr.length; // 2
That's why it's preferred not to use for ... in loops when you only want to iterate on the values of the array : you can use for(var i=0,var l=arr.length;i<arr.length;i++) instead.
Yes ,you can access array like an object Only if array keys are string,
var sdf = [];
sdf['asd'] =45;
sdf[32] =8674;
console.log(sdf.asd) // WORKS
console.log(sdf.32) // Error
Array.prototype.forEach is what you want. Just be mindful of browser support or use a framework that corrects for unsupported browsers: http://kangax.github.io/compat-table/es5/#Array.prototype.forEach
for in should be used for iterating over object properties because order is not guaranteed.

Javascript for..in on object property

I have the following JSON:
{"data":[{"id":1,"client_id":1},{"id":2,"client_id":1}]}
I'm trying to do a for...in but something is not going well.
Look at my code:
for (post in data) {
console.log(post.client_id); //return undefined
}
But if I do this:
for (i=0 ; i<data.length; i++) {
console.log(data[i].client_id); //return the correct value
}
Why can't I iterate with for..in in this case?
The value of the "data" property is an array. You need:
for (var i = 0; i < json.data.length; ++i)
console.log(json.data[i].client_id);
That's assuming that the JSON has been parsed and stored in a variable called "json". If it's actually called "data", then it'd be "data.data" instead of "json.data".
You really should not use for ... in loops for real arrays. It's a bad habit for a variety of reasons. Use a numeric index or something like the .forEach() facilities available in newer browsers.
First off, you should NEVER iterate arrays with for/in. That structure iterates properties of the object. You will get all the array elements, but you may also get other iterable properties of the object too.
Arrays should always be iterated with a traditional for loop as in:
for (var i = 0; i < arr.length; i++).
Second off, you have to make sure you're following your own data structure properly. You have this:
var item = {"data":[{"id":1,"client_id":1},{"id":2,"client_id":1}]};
which spread out some more looks like this:
var item = {
"data":[
{"id":1,"client_id":1},
{"id":2,"client_id":1}
]
};
So, your JSON is an object. That object has one property in it called data and that one property's value is an array, which contains objects. You would get the first item in the array with this:
item.data[0]
Or the properties on that object with this:
item.data[0].id
item.data[0].client_id
You would iterate all the items in that array like this:
for (var i = 0; i < item.data.length; i++) {
// access each object in the array
item.data[i].id
item.data[i].client_id
}
it should be like this:
for (var post in data) {
console.log(data[post].client_id); //return undefined
}
this is the correct form to iterate using for... in
for (post in data) {
console.log(post.client_id); //return undefined
}
you're doing this wrong, try it like this:
for (post in data) {
console.log(data[post].client_id); //return undefined
}
you should actually also include a filter, since stuff from the prototype can leak in:
for (post in data) {
if (data.hasOwnProperty(post)) {
console.log(data[post].client_id); //return undefined
}
}
It's because of the structure of your JSON. Notice that you've got an outer object with only a single top-level property: data.
{ "data":
[
{"id":1,"client_id":1},
{"id":2,"client_id":1}
]
}
data doesn't have a client_id property: it's an array of the sub-entries that you're trying to iterate over, and those have client_id properties. This is why your loop that indexes data works.

Javascript: Iterating over array with non-consecutive keys

I need to iterate over an array for which the keys are non-consecutive:
var messages = new Array();
messages[0] = "This is the first message";
messages[3] = "This is another message";
Obviously using the index of a for loop will not work as it depends on the keys being sequential:
for (var i=0 ; i<messages.length ; i++) {
alert(messages[i]); // Will only alert the first message, as i is never equal to 3
}
What is the canonical way of dealing with this, seeing as the for-each syntax is not intended for iterating over values in an array in javascript? Thanks.
The idiomatic way would be to use an object, not an array. Just be sure to check hasOwnProperty to make sure you don't pick up stray things which may have been added to the prototype.
var messages = { };
messages[0] = "This is the first message";
messages[3] = "This is another message";
for (var i in messages) {
if (messages.hasOwnProperty(i))
alert(messages[i]);
}
Or, the more modern way would be to use Object.keys
Object.keys(messages).forEach(prop => {
alert(messages[prop]);
});
Be sure to transpile that code with Babel if you plan on running it in older browsers like IE.
for(var i in messages)
{
console.log(messages[i]);
}
You could ignore the undefined properties...
for (var i=0 ; i<messages.length ; i++) {
if(messages[i] !== undefined)
alert(messages[i]);
}
Or use forEach, which will ignore undefined undeclared properties...
messages.forEach(function(v,i) {
alert(v);
});
Simple! if the array has regular gaps between the indices do this:
for (var i = 0 ; i < messages.length; i += gap) {
alert(messages[i]); // Will only alert the messages at the regular interval/gap
}
You can use each() jQuery method to do this.
$.each(messages, function(index, val){
alert(val);
});
From jQuery docs
each()
A generic iterator function, which can be used to seamlessly iterate
over both objects and arrays. Arrays and array-like objects with a
length property (such as a function's arguments object) are iterated
by numeric index, from 0 to length-1. Other objects are iterated via
their named properties.
When you create an array and give it values at 0 and 3, undefined values are created at 1 and 2. try this:
$.each(messages, function(i,val) {
if (val) {
alert(val);
}
});
For a use case such with the assumptions:
array.length >== 1
(i.e: an array with meaningful data in it already)
Where you are interested in the data from array[1], array[15], array[45] etc
You can do something similar to:
var array = ["you","will","become","strong","with","the","codes","padawan"];
var values = [1,5,7];
for (var i = 0; i < values.length; i++){
var cypher = values[i];
console.log(array[cypher]);
}
//will, the, padawan
Or perhaps something more meaningful such as:
for (var i = 0; i < values.length; i++){
var cypher = values[i];
aService.aFn.(array[cypher],cb);
}
//calls aService.aFn separately for each value array[1] , array[5] , array[7] passed as args

Categories

Resources