Array Self-Referencing Map - Very Bizarre Result - javascript

While experimenting with some different methods for generating JavaScript arrays, I stumbled on a weird result. Using map to push an array of self-references (DEMO):
a=[1,1,1,1,1,1,1,1,1,1];
a=a.map(a.push,a);
I get the following result (in Chrome):
[13,16,19,22,25,28,31,34,37,40]
Can anyone explain why?

For each element in a, push is being called with that element, the index of that element, and the array being traversed. For each element in the array, then, we add these three additional elements. This accounts for the length increasing by three for each element in the original array. The result of push is the length of the array after the elements are added, thus the resulting array (from map) is an array holding the lengths of the a array after each push callback is completed.
See the documentation for map and push.

It has something to do with the return value of push being the new length. Not sure why it incrementing by 3.
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/push
Returns
The new length property of the object upon which the method was called.

Related

how is this function mutating non-returned array

I'd ask a better question, but I don't don't know how. Thanks for your help.
***ISSUE: I'm sending array vari to a function and it's coming back changed even though I didn't return it or even use same variable name. Desired function: variable vari does not change
I've logged the function and isolated the change to the [].forEach() statement below, noted with ***. I send vari but return varis and assign to new variable sum. How does this change the vari variable?
//this is what calls the sub function using vari, an array, 411.0, 13.0
var sum = doSum1(vari);
function doSum1(vari0) {
// called from doSum1
// grab Variance data // ALL COLUMNS FOR NOW // fix loc/stat columns below
var vstat = vari0[0].indexOf('Status');
vari1 = vari0.filter(r=>r[vstat]); // to ensure indexOf works, and speed processing
var vhdr = ['Campaign ID','Campaign','Site ID','Site','Placement','Placement ID','Date','DCM Imp','Upw Imp','Tag Location','Status','Site Count','Site Imp'];
// move loc and status over in place of variance and percent (loc/stat will be site ct/imp)
varis=[];
// *** THIS FOREACH CHANGES varis AND vari. Not sure how... see more specifically below
['Not Tracking','Undertracking','Overtracking','Absent in DCM'].forEach(rf=>{
varis.push(vhdr.map(r=>''));
varis[varis.length-1][0]=rf;
varis.push(vhdr);
if(vari1.filter(r=>r[vstat].indexOf(rf)>=0).length==0) {
varis.push(vhdr.map(r=>''));
varis[varis.length-1][0]='none found';
} else {
varis.push(vari1.filter(r=>r[vstat].toString().indexOf(rf)>=0)[0]); // break out of outer []
//fix loc/stat location
//*** MORE SPECIFICALLY, this line in particular changes in vari, not just varis as intended.
varis[varis.length-1].splice(9,4,varis[varis.length-1][11],varis[varis.length-1][12],'','')
}
varis.push(vhdr.map(r=>'')); // trailing blank line
});
return varis;
I tried this in place of the splice as well, but same result... just not sure how varis is changing vari...
varis[varis.length-1][9] = varis[varis.length-1][11];
varis[varis.length-1][10] = varis[varis.length-1][12];
varis[varis.length-1][11] = '';
varis[varis.length-1][12] = '';
vari is a 2D array. That means that every element in vari is an array as well, and as such passed by reference and subject to mutation.
The Array.splice() method mutates its argument array. In the code, each varis[varis.length-1].splice() call modifies an array object that is copied from vari1 by reference, and therefore also vari0 whose elements are array objects that are copied to vari1 by reference. This is what causes vari to mutate.
To avoid the issue, use one these patterns:
var vari1 = vari0.map(row => row.slice()).filter(r => r[vstat]);
or
var vari1 = vari0.map(row => row.map(value => value)).filter(r => r[vstat]);
The patterns use Array.map() and Array.slice()to get a shallow copy of the 2D array referenced by vari0 (i.e., vari).
The first map() creates a new array of that contains the rows of vari0. The rows are arrays and therefore mutable, so a slice() or another map() is required to copy the rows into new arrays as well.
Note that the copy is shallow, which means that only primitive values such as text strings and numbers are copied by value. Your comments indicate that the rows of vari only contain primitives, so the pattern will make a copy that is safe to modify and will not mutate vari. Were the rows of the vari 2D array contain yet more arrays or other objects, the would be copied by reference and therefore still be subject to mutation.
Note that Array.splice() and Array.slice() are very different from each other. The Array.splice() method mutates its argument array. The Array.slice() method creates a shallow copy of the array, and is in fact often used to safely copy 1D arrays that contain primitives. In your use case, the vari array does not contain primitives but arrays, so we need to call slice() within map() to copy the primitive values in the second level of the 2D array.
In the general case, deep cloning an array or another object is surprisingly complex. The patterns above are probably the simplest way to do it in your use case. See What is the most efficient way to deep clone an object in JavaScript?

Why is filter method not deleting all elements of the array?

this.arol.filter(x=>x.length!==0
?(this.arol.splice(this.arol.indexOf(x),1))
:!true)
I was trying to change it many different ways, but it still does not delete all elements of the array, it always leaves 1 or 2 behind deleting most of them.... I think the problem is with the condition... We are checking if the length of array elements is not 0 (which are all strings)...
Don't try to splice while filtering - instead, return from the filter callback a truthy or falsey value, depending on whether you want to include the item being iterated over in the new array, and use the resulting array that gets returned from .filter:
this.arol = this.arol.filter(x => x.length !== 0);
^^^^^^^^^^^^
If you want to maintain same outer array reference and mutate original you can splice in a loop if you work from end to start so as not to affect indexing you haven't arrived at yet as you change the array length:
const arr = [[1],[],[2]]
arr.reduceRight((_,c,i) => c.length || !!arr.splice(i,1))
console.log(arr)
The problem is you are trying to splice at the same time you are using filter. Filter does not remove elements from your array, it creates a new array with the filtered data, as described here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
You can assign the result to the same array as suggested by CertainPerformance.
If Splice is removed from that code then it will not delete elements. this.arol.splice(this.arol.indexOf(x),1) here it will iterate through every element inside that array and x will be current iteration which will splice from the array as you have written splice method. Remove splice it will work fine.

Array length is not correct in javascript

I have an array like below
arr=[];
arr[0]={"zero": "apple"};
arr[1]={"one": "orange"};
arr["fancy"]="what?";
but i am getting length as 2 when i do console.log(arr.length) even though i am able to console all the values .
and not able to get all values while doing console.log(JSON.stringify(arr))
What is the issue here.
here is the link to fiddle fiddle
.length is a special property in Javascript arrays, which is defined as "the biggest numeric index in the array plus one" (or 2^32-1, whatever comes first). It's not "the number of elements", as the name might suggest.
When you iterate an array, either directly with for..of or map, or indirectly with e.g. JSON.stringify, JS just loops over all numbers from 0 to length - 1, and, if there's a property under this number, outputs/returns it. It doesn't look into other properties.
The length property don't work as one will expect on arrays that are hashtables or associative arrays. This property only works as one will expect on numeric indexed arrays (and normalized, i.e, without holes). But there exists a way for get the length of an associative array, first you have to get the list of keys from the associative array using Object.keys(arr) and then you can use the length property over this list (that is a normalized indexed array). Like on the next example:
arr=[];
arr[0]={"zero": "apple"};
arr[1]={"one": "orange"};
arr["fancy"]="what?";
console.log(Object.keys(arr).length);
And about this next question:
not able to get all values while doing console.log(JSON.stringify(arr))
Your arr element don't have the correct format to be a JSON. If you want it to be a JSON check the syntax on the next example:
jsonObj = {};
jsonObj[0] = {"zero": "apple"};
jsonObj[1] = {"one": "orange"};
jsonObj["fancy"] = "what?";
console.log(Object.keys(jsonObj).length);
console.log(JSON.stringify(jsonObj));
From MDN description on arrays, here, "Arrays cannot use strings as element indexes (as in an associative array) but must use integers."
In other words, this is not Javascript array syntax
arr["fancy"]="what?";
Which leads to the error in .length.

Javascript slice isn't giving me correct array length values

Why does it say length 1 instead of 4?
The following is what I'm trying to push and slice. I try and append items.image_urls and slice them into 5 each.
items.image_urls is my dictionary array.
var final_push = []
final_push.push(items.image_urls.splice(0,5))
console.log(final_push.length)## gives me 1...?
var index = 0
final_push.forEach(function(results){
index++ ##this gives me one. I would need 1,2,3,4,5,1,2,3,4,5. Somehting along that.
}
items.image_urls looks like this:
It's an iteration of arrays with image urls.
In your example items.image_urls.splice(0,5) returns an array of items removed from items.image_urls. When you call final_push.push(items.image_urls.splice(0,5));, this whole array is pushed as one item to the final_push array, so it now looks like [["url1", "url2", "url3", "url4", "url5"]] (2-dimensional array). You can access this whole array by calling final_push[some_index].
But what you want instead is to add every element of items.image_urls.splice(0,5) to the final_push. You can use a spread operator to achieve this:
final_push.push(...items.image_urls.splice(0,5));
Spread syntax allows an iterable such as an array expression or string
to be expanded in places where zero or more arguments (for function
calls) or elements (for array literals) are expected
This is exactly our case, because push() expects one or more arguments:
arr.push(element1[, ...[, elementN]])
And here is an example:
let items = {
image_urls: ["url1", "url2", "url3", "url4", "url5", "url6", "url7", "url8", "url9", "url10"]
};
let final_push = [];
final_push.push(...items.image_urls.splice(0,5));
console.log(final_push.length);
console.log(JSON.stringify(final_push));
console.log(JSON.stringify(items.image_urls));
Note: do not confuse Array.prototype.slice() with Array.prototype.splice() - the first one returns a shallow copy of a portion of an array into a new array object while the second changes the contents of an array by removing existing elements and/or adding new elements and returns an array containing the deleted elements.
That seems to be a nested array. So if you would access index 0, and then work on that array like below it will probably work:
console.log(final_push[0].length); //should print 4
The author is mixing up splice and slice. Probably a typo :)
You start at the beginning (0) and then delete 5 items.

JQuery - Push method JSONArray

I'm doing the push method between two Arrays to create a bigger Array. The two simple arrays I want to fix are:
[{"id":"11"},{"color":"blue","value":"14"}]
[{"id":"11"},{"color":"green","value":"25"}]
The code to push the two arrays is:
var totjunt = $('body').data('cesta_list').push(array_of_bought_colors_new);
With $('body').data('cesta_list'); I save the first array and then i try to push the second array.
Using console.log(JSON.stringify(totjunt)); I print the value throught the console but the problem is that the console prints only a number 2.
You're logging the result of the push() call, not the resulting array. Try this:
$('body').data('cesta_list').push(array_of_bought_colors_new);
var totjunt = $('body').data('cesta_list');
More specifically, push() returns the length of the new array, not the array itself.
.push doesn't return a new array. It returns the array's new length. The array is updated in-place.

Categories

Resources