Array.length gives incorrect length - javascript

If I have an array having object as values at the indices like:
var a = [];
a[21] = {};
a[90] = {};
a[13] = {};
alert(a.length); // outputs 91
I have found a workaround to get the actual length:
function getLength(arr) {
return Object.keys(arr).length;
}
var a = [];
a[21] = {};
a[90] = {};
a[13] = {};
alert(getLength(a));
But, why does JS gives incorrect length when objects are stored at random indices? It just adds 1 to the largest index found on an array. Like in the above example, 90 was the largest index. It just adds 1 and gives 91 as output. Demonstration

That's because length gives you the next index available in the array.
DOCS
arrayLength
If the only argument passed to the Array constructor is an integer between 0 and 2^32-1 (inclusive), this returns a new JavaScript array with length set to that number.
ECMA Specifications
Because you don't have inserted any element in the other keys than 21, 90, 13, all the remaining indexes contains undefined. DEMO
To get actual number of elements in the array:
var a = [];
a[21] = {};
a[90] = {};
a[13] = {};
var len = 0;
for (var i = 0; i < a.length; i++) {
if (a[i] !== undefined) {
len++;
}
}
document.write(len);
Shorter version
var a = [];
a[21] = {};
a[90] = {};
a[13] = {};
for (var i = 0, len = 0; i < a.length; i++, a[i] !== undefined && len++);
document.write(len);
DEMO
EDIT
If the array contains large number of elements, looping to get its length is not the best choice.
As you've mentioned in the question, Object.keys(arr).length is the best solution in this case, considering that you don't have any properties added on that array. Otherwise, the length will not be what you might be expecting.(Thanks To #RobG)

The array in JavaScript is a simple zero-based structure. The array.length returns the n + 1 where n is the maximum index in an array.
That's just how it works - when you assign 90'th element and this array's length is less than 90, it expands an array to 90 and sets the 90-th element's value. All missing values are interpreted as null.
If you try the following code:
var a = [];
a[21] = {};
a[90] = {};
a[13] = {};
console.log(JSON.stringify(a));
You will get the following JSON:
[null,null,null,null,null,null,null,null,null,null,null,null,null,{},null,null,null,null,null,null,null,{},null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,{}]
Moreover, array.length is not a readonly value.
If you set a length value less than the current, then the array will be resized:
var arr = [1,2,3,4,5];
arr.length = 3;
console.log(JSON.stringify(arr));
// [1,2,3]
If you set a length value more than the current, then the array will be expanded as well:
var arr = [1,2,3];
arr.length = 5;
console.log(JSON.stringify(arr));
// [1,2,3,null,null]
In case you need to assign such values, you can use JS objects.
You can use them as associative array and assign any key-value pairs.
var a = {};
a[21] = 'a';
a[90] = 'b';
a[13] = 'c';
a['stringkey'] = 'd';
a.stringparam = 'e'; // btw, a['stringkey'] and a.stringkey is the same
console.log(JSON.stringify(a));
// returns {"13":"c","21":"a","90":"b","stringkey":"d","stringparam":"e"}
console.log(Object.keys(a).length);
// returns 5

Because that's the behavior of Array.length as described in the ECMAScript spec.
The length property of this Array object is a data property whose value is always numerically greater than the name of every deletable property whose name is an array index.
So Array.length is always the last item's index + 1.

It's because you have a[90] as largest index so the index is starting from 0 to 90 becomes 91 in length.
And where you didn't pass the values like a[80], etc. javascript will store them as hole i.e. for eg [1, , 3, , ,90] where commas are used indicates the hole in array.
If you try to access those values then you'll get undefined.

Related

Why are the property of the values stored in my object strings, how can I access them to increase the occurrence?

Creating a function call frequency and passing an array. I'm taking the values that are in the array which are integers and storing them inside of an object and my end goal is to check the occurrence of every number that has been seen and if they been seen I will increase the count. Why are the property are set as strings?, how can I access the integers to increase the count. I want to increase my understanding of objects.
function frequency(array) {
let object = {}
let count = 0
let num
let temp = []
for (var i = 0; i < array.length; i++) {
num = array[i]
object[num] = count++
console.log(object);
}
}
console.log(frequency([1, 2, 3, 4]));
Javascript object properties are string:any key-value pairs. You see your numbers as string keys because of this. When you set a property with an integer key (or any other non-string value), it is first converted into a string.
To get the correct count of occurrences of your numbers, you should be incrementing the key corresponding to the value, rather than just incrementing your count variable. I also added the typeof line to check and make sure that key-value pair has been previously set. If not, it creates one and gives it a value of 1, since this is the first time we've seen that number.
function frequency(array) {
let object = {}
let count = 0
let num
let temp = []
for (var i = 0; i < array.length; i++) {
num = array[i]
if(typeof(object[num]) != "undefined")
object[num]++;
else
object[num] = 1;
}
console.log(object);
}
frequency([1, 2, 3, 4, 4, 4, 3, 5, 1]);

Updating array based on newer objects in another array

I have a question that is hard to describe. It's a tale of two arrays.
I have one array holding 50 objects. Per object, I want to call its specific property "IDnumber".
The second array is one that has 5 variables containing all of the mentioned 50 objects's "IDnumber" property, with each variable having a set of 10 IDnumbers: var1 is the IDnumbers of the first 10 objects, var2 is is the IDnumbers of the second set, and so on until the 50th object's ID number.
This is where it gets hard.
If I want to add in a new object in the first array, so that there'll be 51 objects, then I want to update the second array with a 6th variable, that contains all the remaining objects' IDnumbers (in this case just the 1). I want array2's length to be dependent on array1's length.
var arr1 = [], obj = {"IDNumber": "1"};
//this is an example array, all objects have the same ID here, but not in my real array
for (var i = 0; i < 51; i++) {
arr1.push(obj);
}
var var1 = [arr1[0]["IDNumber"], arr1[1]["IDNumber"], arr1[2]["IDNumber"], arr1[3]["IDNumber"], arr1[4]["IDNumber"], arr1[5]["IDNumber"], arr1[6]["IDNumber"], arr1[7]["IDNumber"], arr1[8]["IDNumber"], arr1[9]["IDNumber"]];
//the other remaining possible variables.
var arr2 = [var1, ...]; //No clue as how to take that on
How do I do that? How do I create an array that updates itself with newer possible variables like that? Each variable must have a max of 10 objects' IDnumbers.
Suppose array1 contains your array of objects. The other one is array2 containing an array of arrays, each sub array has length 10 like you stated
You can split array1 into groups of 10 and put in array2 like this
function slice10(arraysOfObject) {
var array2 = [];
var leftOver = arraysOfObject.length % 10;
var groupsOfTen = (arraysOfObject.length - leftOver)/10;
for (var i = 0; i < groupsOfTen; i++) {
array2.push([]);
for (var j = i*10; j < i*10 + 10; j++)
array2[i].push(arraysOfObject[j]["IDNumber"]);
}
//now take care of the leftover
if (leftOver > 0) {
array2.push([]);
for (var i = groupsOfTen*10; i < arraysOfObject.length; i++)
array2[array2.length-1].push(arraysOfObject[i]["IDNumber"]);
}
return array2;
}
You could create a function to deal with adding an object to the two different data structures, and use that function also for adding the first 50 objects:
function addObject(arr1, arr2, obj) {
if (arr1.length % 10 == 0) arr2.push([]);
arr1.push(obj);
arr2[arr2.length-1].push(obj.IDNumber);
}
var arr1 = [], arr2 = [];
for (var i = 0; i < 51; i++) {
addObject(arr1, arr2, {"IDNumber": i + 1000}); // some dummy ID value
}
console.log(arr2);
.as-console-wrapper { max-height: 100% !important; top: 0; }

String json has null values

I am trying to stringify my json -
for (var i = 0 ; i < lines.length ; i++) {
var label = lines[i];
var value = 1;
item = [];
item["label"] = label;
item["value"] = value;
jsonObj.push(item);
}
var jsonString = JSON.stringify(jsonObj);
During iteration, both label and value are being assigned accordingly with the correct values.
However jsonString is full of null values, why is this the case?
It should be item = {}; and not item = [];.
The first is the object literal, and the second is the array literal.
For good measure, do var items = {};
The case is that you create an array item = [] then set its string properties.
Whereas JSON.stringify expects that something that looks like an array is an array, so it does not even try to iterate over its non-numeric properties.
The solution for you would be to replace it with an object {}
The excerpt from the specification:
If Type(value) is Object, and IsCallable(value) is false
If the [[Class]] internal property of value is "Array" then
Return the result of calling the abstract operation JA with argument value.
followed by
Let len be the result of calling the [[Get]] internal method of value with argument "length".
Let index be 0.
Repeat while index < len
Let strP be the result of calling the abstract operation Str with arguments ToString(index) and value.
If strP is undefined
Append "null" to partial.
Else
Append strP to partial.
Increment index by 1.
References:
15.12.3 stringify
15.12.3 stringify JA
As pointed out, you'll need to make your item an object. Here's a JSFiddle to get you started with an example.
var item;
var lines = ["a","b","c"];
var jsonObj = {};
jsonObj.items = [];
for (var i = 0 ; i < lines.length ; i++) {
var label = lines[i];
var value = 1;
item = {};
item["label"] = label;
item["value"] = value;
jsonObj.items.push(item);
console.log(jsonObj);
}
var jsonString = JSON.stringify(jsonObj);
console.log(jsonString);

Two for loops one ascending one descending creates two different data structures

I have an array that I am looping twice, one im looping from pointer 0 and up and the other im looping backwards.
But they produce different array layouts in console.log which i find strange .. this is what i have:
//array has a length of 3 [0,1,2]
var data = [],
data2 = [];
for(var i = 0; i < array.length-1; i++){
data[i] = {};
data[i].test = 'Hello';
}
for(var i = array.length - 1; i > 0; i--){
data2[i] = {};
data2[i].test = 'Hello';
}
console.log(data);
console.log(data2);
Now in my console log they come out different like this:
So why does the second one have a length of 3 but only 2 objects? And why does the second console.log have numbers in front of the objects unlike the first one ?
The length of an Array is simply the max(index) + 1.
When you create a "sparse" array, the intermediate indexes are still treated as existing.
console.log(JSON.stringify(data2));
// [null, {"test":"Hello"}, {"test":"Hello"}]
Even if the instance doesn't itself have them.
console.log('0' in data2);
// false
If you want a count of keys that were used, you could get the length of its Object.keys():
console.log(Object.keys(data2).length);
// 2
Possibly filtering to just indexes.
Object.indexes = function (target) {
return Object.keys(target).filter(function (key) {
return key === String(parseInt(key, 10));
});
};
console.log(Object.indexes(data2).length);
Note that these snippets expect an ES5-based engine or polyfills.
Your inner loop has this continuing condition:
i > 0
Which stops it before it gets to the data2[0] position.
And so, you're setting data2[2] and then data2[1]. data2[0] remains unset but it's still in the array (it's still the pointer position).
This is what you're looking for
for(var i = array.length - 2; i >= 0; i--){
data2[i] = {};
data2[i].test = 'Hello';
}

Javascript combine arrays or strings

I have the following dynamically generated strings:
var stringA = ["a1", "a2", "a3" ... 'a400' ... 'a600']; // length 600
var stringB = ["b1", "b2", "b3" ... 'b400']; // length 400
How can I get an Array or string of both combined like this:
var myString = ["a1b1", "a2b2", "a3b3" ... "a400b400", "a401" ... "a600"]
You can do something like this:
var result = [], len = Math.max(stringA.length, stringB.length);
for(var i=0; i < len; i++) {
result.push((stringA[i] || "") + (stringB[i] || ""));
}
You can test it out here, the || "" is to prevent getting undefined as a string on the for the array that's shorter. The Math.max() call is to allow either A or B to be longer, it'll iterate to the end of either, just as A is longer in the question.
I don't think there's anything built into the Array object that will do it for you, you'll have to do the loop. The loop is trivial, though:
var index, length;
var result = [];
// assertion: arrayA.length === arrayB.length
result.length = arrayA.length; // Helps performance in some implemenations, harmless in others
for (index = 0, length = arrayA.length; index < length; ++index) {
result[index] = arrayA[index] + arrayB[index];
}
(I've renamed stringA -> arrayA and stringB -> arrayB to avoid confusion.)
If the arrays are different lengths or some of the entries in the arrays are undefined (which is totally possible, JavaScript arrays are sparse), you'll want to handle that in the loop, e.g.:
var index, length, Apresent, Bpresent;
var result = [];
result.length = Math.max(arrayA.length, arrayB.length); // Helps performance in some implementations, harmless in others
for (index = 0, length = result.length; index < length; ++index) {
Apresent = arrayA.hasOwnProperty(index);
Bpresent = arrayB.hasOwnProperty(index);
if (Apresent && Bpresent) {
result[index] = arrayA[index] + arrayB[index];
}
else if (Apresent) {
result[index] = arrayA[index];
}
else if (Bpresent) {
result[index] = arrayB[index];
}
}
If the arrays are sparse and they both happen to be sparse at the same index, the resulting array will also be sparse.
If you do not need to retain the the items in the original 2 arrays then the code below works really well. It does not have a predetermined limit to the number of iterations in the for loop which means that the stringA and StringB array could continue to grow in size while this code is running.
var myString = [];
for(var answer; answer = (stringA.shift() || "") + (stringB.shift() || "");) {
myString.push(answer);
}
var myString = [];
for (var i=0;i<stringA.length;i++){
myString[i] = stringA[i] + stringB[i];
}

Categories