Count of "Defined" Array Elements - javascript

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)))

Related

Is there any point in creating a custom iterator for an array in node.js?

I need to parse an 80GB+ CSV file, and thought this a good opportunity to understand iterators in JavaScript (and then probably use an existing library such as csv-iterator, fast-csv, etc).
Looking at an iterator example on MDN HERE, I see this code:
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{done: true};
}
};
}
Which is pretty self-explanatory. I can create an iterator for an array:
var iteratorForMyArray = makeIterator([1,2,3,4,5,6,7,8,9, etc])
And then I can use my shiny new iterator to 'iteratively' pull out values of my array:
var val0 = iteratorForMyArray.next().value
var val1 = iteratorForMyArray.next().value
etc
I can see why this is useful when parsing a CSV file. I'm wondering if there is any point in creating such an iterator for a simple array?
I often find that while simplified examples are useful for understanding, it's sometimes hard to see when the example is only useful as an example vs is actually useful in programming.
Because JavaScript already provides 2 mechanisms for creating iterators over array structures:
1: Basic for loop
for (let i = 0, i < [1,2,3,4,etc]; i++) {
...
}
2: Array.prototype.forEach
[1,2,3,4,etc].forEach(function(val, i, arr) {
...
})
(which I have just seen is slow on this site)
Questions:
Does my custom iterator offer anything that these iterators don't?
Do both these 'in-house' iterators create 'iterators' (i.e. what I understand as sequential pointers to values in data structures)? Or am I off by miles...
ForEach guarantees order, as well as it'll skip invalid indexes etc. Your iterator doesn't provide anything extra when compared to a standard for loop, it'll just be slower, as you're calling a function to get the next item, where a basic for loop doesn't have this overhead. Use a for loop and cache the length to start for better performance.
One good feature of iterators is that each call gets the next element, whereas forEach it's all or nothing (you can't exit early), and with both a for loop and forEach all the logic must be inside the loop.
Consider an array iterator like:
function arrayIterator(array) {
// Get array indexes as an array
var indexes = Object.keys(array)
.filter(
// Test for valid Array index property
key => key == +key && key >= 0 && key.length == String(+key).length)
// Sort as numbers
.sort(function(a, b){return a-b});
// Parameters held in closure
var current = 0;
var count = 0;
var maxCount = indexes.length;
// Return iterator function
return function() {
return ++count > maxCount? 'done' : array[indexes[current++]];
};
}
var arr = [0,1,,,4,5,6];
// Add non-index numeric property
arr['01'] = '01';
// Add indexable numeric property
arr['10'] = '10';
// Add some other property
arr.foo = 'foo';
var x = arrayIterator(arr);
console.log(x()); // 0
console.log(x()); // 1
console.log(x()); // 4
console.log(x()); // 5
console.log(x()); // 6
console.log(x()); // 10
console.log(x()); // 'done'
console.log(x()); // 'done'
I'm sure there's more to be done in regard to checking valid indexes and also testing that an index still exists when the iterator is called, but is shows the concept.
There also needs to be some documentation about what happens when the array is modified between calls.

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

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

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.

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")

Iterate over defined elements of a JS array

I'm using a JS array to Map IDs to actual elements, i.e. a key-value store. I would like to iterate over all elements. I tried several methods, but all have its caveats:
for (var item in map) {...}
Does iterates over all properties of the array, therefore it will include also functions and extensions to Array.prototype. For example someone dropping in the Prototype library in the future will brake existing code.
var length = map.lenth;
for (var i = 0; i < length; i++) {
var item = map[i];
...
}
does work but just like
$.each(map, function(index, item) {...});
They iterate over the whole range of indexes 0..max(id) which has horrible drawbacks:
var x = [];
x[1]=1;
x[10]=10;
$.each(x, function(i,v) {console.log(i+": "+v);});
0: undefined
1: 1
2: undefined
3: undefined
4: undefined
5: undefined
6: undefined
7: undefined
8: undefined
9: undefined
10: 10
Of course my IDs wont resemble a continuous sequence either. Moreover there can be huge gaps between them so skipping undefined in the latter case is unacceptable for performance reasons. How is it possible to safely iterate over only the defined elements of an array (in a way that works in all browsers and IE)?
Use hasOwnProperty within for ... in to make sure that prototype additions aren't included:
for (var item in map)
if (map.hasOwnProperty(item)) {
// do something
}
There are three issues:
You should not use for...in to iterate arrays.
You are using the wrong data type for your requirements.
You are not using for...in correctly.
If you want to have something like a hash table then use a plain object:
var map = {};
map[123] = 'something';
map.foo = 'bar';
// same as map['foo'] = 'bar';
//...
It looks like an array, but it is not. It is an object with property 123. You can use either dot notation obj.key (only if the key is a valid identifier - 123 would not be valid so you have to use the following notation) or array notation obj['key'] to access object properties.
It seems that an object would be a more appropriate data structure.
But even then you should make a call to hasOwnProperty (every time you use for...in):
for(var key in obj) {
if(obj.hasOwnProperty(key)) {
//do something
}
}
This checks whether a property is inherited from the prototype (it will return false then) or is truly an own property.
Use the EcmaScript 5 builtin Object.keys, and on non ES5 browsers, define it thus:
Object.keys = function (o) {
var keys = [];
var hasOwnProp = Object.prototype.hasOwnProperty;
if (Object.prototype.toString.call(o) === '[object Array]') {
for (var k in o) {
if (+k === (k & 0x7fffffff) && hasOwnProp.call(o, k)) {
keys[keys.length] = k;
}
}
keys.sort(keys, function (a, b) { return a - b; });
} else {
for (var k in o) {
if (hasOwnProp.call(o, k)) {
keys[keys.length] = k;
}
}
}
return keys;
};
1) use an object like already suggested, it is by far the best solution.
2) if you for some reason need to use an array - don't be scared looping over it with
for(var i, len = arr.length;len < i;i++)
it's very very fast.
3) don't use $.each or similar methods if you want performance - they create a new callstack for every iteration, which is a huge overhead.
Don't use an array. Use an object hash instead
var map = {};
map[key] = value;
...
for (var key in map) {
do something to map[key]
}
You can't do a lot without actually doing a check to see if the value is undefined and then doing operation a or operation b. It would be better to use a predicate to determine if the value is undefined:
x = $.grep(x, function(v, i) { return (typeof(v) != "undefined"); });
There isn't. The only way would be to omit the items from the collection completely, any solution you come up with would still have to do a test on each element for the value.
You could come up with different methods of adding the items key/value to object literals or what have you, but you would still need to omit undefined entries if you do not wish to enumerate over them.

Categories

Resources