Why array.indexOf(undefined) doesn't work if array is sparse - javascript

I'm new at JavaScript and there is one thing that bothers me. I have got a very simple code:
var a = [];
a[1] = 1;
i = typeof(a[0]);
index = a.indexOf(undefined);
len = a.length;
console.log(a);
console.log("\n" + len);
console.log("\n" + i);
console.log("\n" + index);
My question is: Why indexOf returns -1, instead of 0. I know that this method compare by ===, but I used as a parameter keyword undefined. If I change method parameter to "undefined" it also doesn't work (but this for me it's obvious). Can someone explain me this and tell what is the simpliest way to find undefined value in array?

This will in fact find an undefined value in an array, the problem is that your array a doesn't have any undefined values in it, so it returns -1 meaning it did not find any. Your array looks like:
[*uninitialized*, 1]
The fact that you put nothing in the first position doesn't mean it's populated with an undefined, it simply is not initialized/does not exist.
If you did something like:
var a = [undefined, 1];
var index = a.indexOf(undefined);
console.log(index);
It will in fact print 0 as expected.
Edit: to answer your question of how to find an uninitialized value, do the following
var a = [];
a[1] = 1;
for(var i = 0; i < a.length; i++){
if(a[i] === undefined){
console.log(i);
}
}
This will print the index of uninitialized array values. The reason this actually works unlike indexOf is that a[i] will evaluate to undefined if:
(1) The element exists and it has the value undefined, or
(2) The element doesn't exist at all. indexOf however will skip these "gaps" in the array.

In general, arrays in JavaScript are sparse – they can have holes in them (That's why indexOf() returns -1 ), because an array is simply a map from indices to values. The array you are looking for is called dense it looks like
var a = Array.apply(null, Array(3))
or
var a = Array(undefined, undefined, undefined)
a.indexOf(undefined) //0
Please have a look at this post, i hope it will help you

Related

I have an issue with removing an object key with a for in loop

I'm using a for x in loop to check if a value is == to [] and if so remove the property using remove but it just does not seem to work.
const whosOnline = (a) => {
var obj = { online:[],
offline:[],
away:[] };
for(let i = 0; i < a.length; i++){
if(a[i].lastActivity > 10 && a[i].status == 'online'){obj.away.push(a[i].username)}
else if(a[i].status == 'offline'){obj.offline.push(a[i].username)}
else{obj.online.push(a[i].username)}
}
for(let x in obj){
console.log(obj[x])
if(obj[x] === []){delete obj[x]}}
return obj
}
you were close, however you also need to reference the array index for each object key value. Comments explaining this in the code below.
var obj = { online:[],
offline:[],
away:[] };
for(var x in obj){
if(!obj[x][0]){ // The 0 is the index inside the online array, next loop it will be the offline array and then the away array.
console.log('The array is empty');
// Do what you want to do now that it is empty
// This will continue to loop through and check the value of all the keys in the object.
}
}
console.log('done');
Good luck -
Mitch from
https://spangle.com.au
Using some debugging (simply testing if a console.log gets printed for instance) you find that your if-condition is never true.
This is because you test if an array equals a newly created empty array. This can never be the case, because objects are compared by object reference instead of value.
Instead you want to probably test to see if your array is empty by doing ‘if(obj[x].length===0)’ (or shorter: ‘if(!obj[x].length)’)

Weird behavior of javascript

Please see attached screenshot. See pendingApp property of Object. when I am debugging in eclipse then pendingApp show array of object, Which is correct! But when I am JSON.stringify(object) then showing me empty Array.
Please let me know reason of this behavior. I think I am not aware with any Java-Script thought/concept ?? :P :)
When I will save this Object into DB then blank array of pendingApp will be stored !!
var pending_app = [];
var new_record = {"pendingApp" : [], "installedApp" :[] };
....SOME CODE+conditions HERE....
pending_app[appId] = {'action' : action };
new_record.pendingApp = pending_app;
// create app-config data
return app_model.create(new_record); //will return promise object
It's not a weird behaviour but a common mistake of using an Array to store key-value data.
Short Answer : Use a literal Object to store these data
While you can add properties on every objects in Javascript, you cannot iterate over them with the default array mechanisms
for (var i = 0; i < array.length; i++){}
array.forEach();
Simple demonstration :
var array = [];
array["anId"] = 1;
array.length; // 0
array[4294967295] = 1; // Indice >= unsigned 32-bit Max Value
array.length; // 0
array[4294967295]; // 1
So JSON.stringify with the ECMAScript 5 Specification will use the Array mechanism to iterate over all items and will find nothing.
Unlike Objects that you can list properties with
Object.keys(array); // ["anId"]

Using array.splice inside Array prototype

Array.prototype.move = function(oldIndex, newIndex) {
var val = this.splice(oldIndex, 1);
this.splice(newIndex, 0, val[0]);
}
//Testing - Change array position
var testarray = [1, 2, 3, 4];
testarray.move(3, 0);
console.log(testarray);
This produces an error "this.splice is not a function" yet it returns the desired results. Why?
Array.prototype.move = function(oldIndex, newIndex) {
if(Object.prototype.toString.call(this) === '[object Array]') {
if(oldIndex && typeof oldIndex == 'number' && newIndex && typeof newIndex == 'number') {
if(newIndex > this.length) newIndex = this.length;
this.splice(newIndex, 0, this.splice(oldIndex, 1)[0]);
}
}
};
For some reason, the function is being called by the called by the document on load (still haven't quite figured that one out). I added a few checks to verify that this = an array, and then also reset the new index to be equal to the total size if the supplied int was greater than the total length. This solved the error issue I was having, and to me is the simplest way to move objects around in an array. As for why the function is being called onload must be something to do with my code.
You don't need the placeholder variable-
Array.prototype.move = function(oldIndex, newIndex) {
this.splice(newIndex, 0, this.splice(oldIndex, 1)[0]);
}
var a=[1,2,3,4,9,5,6,7,8];
a.move(4,8);
a[8]
/* returned value: (Number)
9
*/
Adding properties to built–in objects is not a good idea if your code must work in arbitrary environments. If you do extend such objects, you shouldn't use property names that are likely to be used by someone else doing the same or similar thing.
There seems to be more than one way to "move" a member, what you seem to be doing can be better named as "swap", so:
if (!Array.prototype.swap) {
Array.prototype.swap = function(a, b) {
var t = this[a];
this[a] = this[b];
this[b] = t;
}
}
I expect that simple re-assignment of values is more efficient than calling methods that need to create new arrays and modify the old one a number of times. But that might be moot anyway. The above is certainly simpler to read and is fewer characters to type.
Note also that the above is stable, array.swap(4,8) gives the same result as array.swap(8,4).
If you want to make a robust function, you first need to work out what to do in cases where either index is greater than array.length, or if one doesn't exist, and so on. e.g.
var a = [,,2]; // a has length 3
a.swap(0,2);
In the above, there are no members at 0 or 1, only at 2. So should the result be:
a = [2]; // a has length 1
or should it be (which will be the result of the above):
a = [2,,undefined]; // a has length 3
or
a = [2,,,]; // a has length 3 (IE may think it's 4, but that's wrong)
Edit
Note that in the OP, the result of:
var b = [,,2];
b.move(0,2);
is
alert(b); // [,2,];
which may not be what is expected, and
b.move(2,0);
alert(b); // [2,,];
so it is not stable either.

Count of "Defined" Array Elements

Given following array:
var arr = [undefined, undefined, 2, 5, undefined, undefined];
I'd like to get the count of elements which are defined (i.e.: those which are not undefined). Other than looping through the array, is there a good way to do this?
In recent browser, you can use filter
var size = arr.filter(function(value) { return value !== undefined }).length;
console.log(size);
Another method, if the browser supports indexOf for arrays:
var size = arr.slice(0).sort().indexOf(undefined);
If for absurd you have one-digit-only elements in the array, you could use that dirty trick:
console.log(arr.join("").length);
There are several methods you can use, but at the end we have to see if it's really worthy doing these instead of a loop.
An array length is not the number of elements in a array, it is the highest index + 1. length property will report correct element count only if there are valid elements in consecutive indices.
var a = [];
a[23] = 'foo';
a.length; // 24
Saying that, there is no way to exclude undefined elements from count without using any form of a loop.
No, the only way to know how many elements are not undefined is to loop through and count them. That doesn't mean you have to write the loop, though, just that something, somewhere has to do it. (See #3 below for why I added that caveat.)
How you loop through and count them is up to you. There are lots of ways:
A standard for loop from 0 to arr.length - 1 (inclusive).
A for..in loop provided you take correct safeguards.
Any of several of the new array features from ECMAScript5 (provided you're using a JavaScript engine that supports them, or you've included an ES5 shim, as they're all shim-able), like some, filter, or reduce, passing in an appropriate function. This is handy not only because you don't have to explicitly write the loop, but because using these features gives the JavaScript engine the opportunity to optimize the loop it does internally in various ways. (Whether it actually does will vary on the engine.)
...but it all amounts to looping, either explicitly or (in the case of the new array features) implicitly.
Loop and count in all browsers:
var cnt = 0;
for (var i = 0; i < arr.length; i++) {
if (arr[i] !== undefined) {
++cnt;
}
}
In modern browsers:
var cnt = 0;
arr.foreach(function(val) {
if (val !== undefined) { ++cnt; }
})
Unfortunately, No. You will you have to go through a loop and count them.
EDIT :
var arrLength = arr.filter(Number);
alert(arrLength);
If the undefined's are implicit then you can do:
var len = 0;
for (var i in arr) { len++ };
undefined's are implicit if you don't set them explicitly
//both are a[0] and a[3] are explicit undefined
var arr = [undefined, 1, 2, undefined];
arr[6] = 3;
//now arr[4] and arr[5] are implicit undefined
delete arr[1]
//now arr[1] is implicit undefined
arr[2] = undefined
//now arr[2] is explicit undefined
Remove the values then check (remove null check here if you want)
const x = A.filter(item => item !== undefined || item !== null).length
With Lodash
const x = _.size(_.filter(A, item => !_.isNil(item)))

Array Out of Bounds: Comparison with undefined, or length check?

this seems to be a common javascript idiom:
function foo (array, index) {
if (typeof array[index] == 'undefined')
alert ('out of bounds baby');
}
as opposed to the more prevalent (in other languages) and conceptually simpler:
function foo (array, index) {
if (index >= array.length)
alert ('boo');
}
I understand that the first case will also work for arrays which have 'gaps' in them, but is that a common enough case to warrant the idiom?
The code sample that prompted this question can be seen here. In this case, when using the 'argument' variable inside a function, isn't it sane to assume that it will be a contiguous array?
The only correct way is to check the index vs. the length.
An element may be assigned the value undefined. It is just silly to use it for a sentinel here. (There may be other, valid and possibly overlapping, reasons for checking for undefined, but not "for an out of bound check" -- the code in the other question will present arguably wrong results when the value of the given arg is really undefined.)
Happy coding.
You can also write:
if (index in array) {
which will return true even if array[index] is set to undefined.
Do not test for undefined. You should use the length of the array. There are cases where it simply does not work to test for undefined because undefined is a legal value for legitimate array entry. Here's a legal JS array:
var legalArray = [4, undefined, "foo"];
And you can access it like this:
var legalArray = [4, undefined, "foo"];
var result = "";
for (var i = 0; i < legalArray.length; i++) {
result += legalArray[i] + "<br>";
}
$("#result").html(result);
Generates this output:
4
undefined
foo
As seen in this jsFiddle: http://jsfiddle.net/jfriend00/J5PPe/
It's not common as far as I know, much more common is:
for (var i=0, iLen=array.length; i<iLen; i++) {
// do stuff
}
You should not compare to undefined, since a member of the array may have been assigned a value of undefined, or may not have been assigned any value.
e.g.
var a = [0,,,,];
alert(a.length); // 4 (or 5 in buggy IE);
a[1] === undefined; // true but not out of bounds
The main reason for using a for loop is that array properties may not be returned in numeric order if a for..in loop is used.
A for..in loop is much more efficient in sparse arrays but the possible out-of-order access must be dealt with if it matters (as must inherited and non-numeric enumerable properties if they should be avoided).
In JavaScript arrays can be sparse - they can contain "holes". For example
const array = new Array(3);
results in an array of three "holes" - not values. So while
const isInBounds = 0 <= index && index < array.length;
correctly identifies whether index is within bounds on array it does not indicate whether there is a value at array[index].
Object.prototype.hasOwnProperty() can be used to determine whether a value exists at an index. It also needs to be noted that different parts of the language can behave quite differently in the presence of "holes".
// ESLint: no-prototype-builtins)
const hasOwnProperty = Object.prototype.hasOwnProperty;
function show(array, index) {
const msg =
0 > index || index >= array.length
? `index ${index} is out of bounds`
: !hasOwnProperty.call(array, index)
? `index ${index} is a hole`
: `index ${index} holds ${array[index]}`;
console.log(msg);
}
const subject = [undefined, , 1];
show(subject, -1);
// "index -1 is out of bounds"
for (let i = 0; i < subject.length; i += 1) show(subject, i);
// "index 0 holds undefined"
// "index 1 is a hole"
// "index 2 holds 1"
show(subject, 3);
// "index 3 is out of bounds"
const toString = (value) =>
value !== undefined ? value.toString() : 'undefined';
// for..of doesn't skip holes
const byForOf = [];
for (const value of subject) byForOf.push(toString(value));
console.log(`Values found by for..of: ${byForOf.join(', ')}`);
// "Values found by for..of: undefined, undefined, 1"
// .forEach skips holes
const byForEach = [];
subject.forEach((value) => byForEach.push(toString(value)));
console.log(`Values found by .forEach: ${byForEach.join(', ')}`);
// "Values found by .forEach: undefined, 1"
// .reduce skips holes
const reducer = (acc, value) => {
acc.push(toString(value));
return acc;
};
const byReduce = subject.reduce(reducer, []);
console.log(`Values found by .reduce: ${byReduce.join(', ')}`);
// "Values found by .reduce: undefined, 1"
// .map preserves holes
const byMap = subject.map(toString);
console.log(`Values found by .map: ${byMap.join(', ')}`);
// "Values found by .map: undefined, , 1"
In that case, the test it to make sure that it does not accidentally add the String "undefined" to the calling String. In the case below, it will actually do just that:
var undefd;
"{0} is dead, but {1} is alive! {0} {2}".format("ASP", undefd, "ASP.NET")
// output as "ASP is dead, but {1} is alive! ASP ASP.NET"
Personally, though, I would probably simply cache the length and then do a numeric comparison.
EDIT
Side note: his method also avoids a NaN check but forces a strict parallel:
// this will fail unless 0001 is cast to a number, which means the method
// provided will fail.
"{0} is dead, but {1} is alive! {0001} {2}".format("ASP", "ASP.NET")

Categories

Resources