Array changes not seen (by other threads?) - javascript

I have a recursive function which performs the following things(among others), in that order:
Prints the array A which is passed as a parameter
Concatenates some new values into it:
A=A.concat(localList);
Prints the array A again
Runs a for loop, each iteration of which calls the function again
While the print sandwich shows correct concatenation, I notice that different(parallel?) instances do not aknowledge the changes other make. Aren't arrays passed as reference?
I've included minimal info because I feel this is some basic fact I'm missing.

Array.concat returns a new instance. In order to keep the reference intact, you can concat a list like so:
yourarray.push.apply(yourarray, newitems)
Or more modern variant:
yourarray.push(...newitems)
To be clear, this will still not work if you're using multiple threads (Workers) since objects passed between Workers are cloned.

Related

Terminology regarding different built-in array methods Javascript

When using the built-in methods available for arrays in Javascript, some methods will act directly on the calling array.
For example, myArray.sort(), will sort myArray in ascending order, alphabetically or numerically.
myArray.sort();
// sort() acts directly on myArray, changing it in its place thereafter
// ... also myArray.reverse() amongst others.
While other methods such as slice(), require there be something, either a variable or other output for it to return its value to...
var need_a_new_array = myArray.slice(10, 21);
// a new placeholder is needed for the results of slice... if not using
// the results immediately (i.e. passing to another function or
// outputting the results)
I was wondering what is the proper terminology for these methods and their differences. I am using arrays as an example here, but I'm sure that the
same probably holds true for objects in general.
I appreciate any help. Thank you.
The correct terms are mutator and accessor.
A mutator method mutates (changes) the object it is called on, while an accessor accesses (and returns) the value of the object it is called on.
You can see examples of the two types by looking at the method listing for Array.prototype. Note that they are divided into categories, two of which are Mutator methods ("These methods modify the array") and Accessor methods ("These methods do not modify the array and return some representation of the array.")
Mutators can not be called on immutable objects.
See also this related question on the software engineering SE: What is the term used to describe a function/method that modifies the object it's called on?
The terms you're looking for are 'immutable' and 'mutable' . Array.prototype.sort is a mutable method in that it 'mutates' (changes) the original array, where as Array.prototype.slice is immutable as it creates a new array with the result and leaves the original array intact.

Object unexpectedly being modified after push into array

I have what seems like it should be a simple operation. For each bridgedSection, I check for a potentialSection with an id that matches the bridged.referenceSection
Then I take that result, parse the HTML on the object with Cherio, make a slight modification (using an id for testing), and then store both the bridgedSection and the modified result on an object, then push that object to the array.
If I log the new object BEFORE pushing, I get the correct object values. If I log it from the array I get incorrect values only for reference.section. bridgedSection is fine, but reference.section matches across all entries in the array.
To say that I'm thoroughly flummoxed is an understatement. Can anyone shed some light on what I am (clearly) doing wrong?
var sectionCount = 0;
bridgedSections.forEach(bridged => {
var obj = potentialSections.find(obj => obj._id == bridged.referenceSection);
$ = cheerio.load(obj.html);
$(".meditor").html(bridged._id);// dropping the id here so it's easy to see if it was updated
obj.html = $.html();
obj.rand = Math.floor(Math.random() * 1000); // can't seem to add to obj either
var thisSection = {
referenceSection: obj,
bridgedSection: bridged,
}
console.log(thisSection) // correct value logged
currentSections.push(thisSection);
sectionCount++;
});
console.log(currentSections);
// this logs an array of the correct length but each
// {}.referenceSection is identical to the last entry pushed above
To try to clarify what both of the above folks are saying, the JavaScript language (like many others) has the concept of references, and makes very heavy use of that concept.
When one variable "refers to" another, there is only one copy of the value in question: everything else is a reference to that one value. Changes made to any of those references will therefore change the [one ...] underlying value (and, be reflected instantaneously in all of the references).
The advantage of references is, of course, that they are extremely "lightweight."
If you need to make a so-called "deep copy" of an array or structure or what-have-you, you can do so. If you want to push the value and be sure that it cannot be changed, you need to make sure that what you've pushed is either such a "deep copy," or that there are no references (as there obviously are, now ...) to whatever it contains. Your choice.
N.B. References – especially circular references – also have important implications for memory management (and "leaks"), because a thing will not be "reaped" by the memory manager until all references to it have ceased to exist. (Everything is "reference counted.")
And, all of what I've just said pretty much applies equally to every language that supports this – as most languages now do.
Javascript is passes function parameters by reference. This means the following happens:
derp = {a:1}
function passedByRef(param){
param['a'] = 2;
}
passedByRef(derp)
console.log(derp['a']) // 2
So when you pass a json object to a function, if you modify said object in the function it will change the original object. You probably want to make a deep copy of bridged before you assign it to thisSection because if you modify the version of bridged later on in thisSection it will modify the original object.
Here is a post that talks about cloning objects or you could look into something like immutable js
I think you need to look into Javascript deep copy.
You are modifying the original object when you modify the second assigned variable, because they are pointing to the same object. What you really need is to duplicate the object, not simply making a pointer to it.
Take a look at this:
https://scotch.io/bar-talk/copying-objects-in-javascript#toc-deep-copying-objects

Should I use array methods like map and filter, if I'm not going to return anything?

In the last year I've been using array methods like map and filter more often instead of the standard for loop on an array. It feels simpler to read and write, and does all the things I'm most likely going to do anyway, like create a local variable.
Often times I don't return anything though. Eslint doesn't like me very much though. According to them, they say you always need a return, otherwise its "probably a mistake"
https://eslint.org/docs/rules/array-callback-return
Why? Is just good practice? What are downsides of a return-less array method?
Been thinking on this for a while. Any insight or thoughts would be great.
Should I use array methods like map and filter, if I'm not going to return anything?
No, you should not.
Why? Is just good practice?
Yes. It is a good practice to use the appropriate iteration method for the type of iteration you are doing. There are numerous ways to iterate for a reason. Pick the appropriate mechanism.
What are downsides of a return-less array method?
Using .map() and .filter() without actually returning anything from the callback have the following downsides:
Your code is misleading. The point of .map() and .filter() is to iterate over the array and produce a new array. When a developer reads some code and sees .map() or .filter() being used, they expect that there should be a returned array. When they don't see it being done that way, they will be confused, will initially feel like they don't understand the code. If I were doing a code review on code like this, I would not approve of code like this.
Your code unnecessarily creates objects that are not used. That's just wasteful and is not a good practice. Instead, use an iteration method that does not produce an output array such as for/of, a regular for loop or .forEach().
Your code won't lint. Linters provide objections to things for a reason. Using .map() or .filter() without returning anything from the callback is, just as the linter says, "probably a programming mistake" because that is not how those functions are designed to be used and there are appropriate alternatives when you don't want a returned array.
So, if you're just trying to do an iteration without creating any resulting array, use for/of or .forEach() or some other iteration scheme that isn't specifically designed to create an output array that you don't want.
First you need to know about the difference between Map/Filter and forEach.
resuming.. forEach is mostly used when you want iterate an array with/as a procedure. check
Map and Filter are related to a callback function applied on every iteration.
The return statement of those is what is going to be evaluated, not the function By the Map/Filter function at the end. Reason why it's needed. Althought JS allows "whatever" And of course you are able to define that function what comes to our understanding as "The filter".
For Filter you can see that "true" and "false" as when the "data" is going to be filtered or not.
basically you can loop with map or forEach/for, the difference are the following:
foreach: This iterates over a list and applies some operation with side effects to each list member, this means that you are transforming THE CURRENT ARRAY you are looping.... or as noticed by #TiagoCoelho, you dont mess with the array at all, just loop thought it.
map: This iterates over a list, transforms each member of that list, and returns another list of the same size with the transformed members, this means that you will get a BRAND NEW ARRAY with the modified items and you will also have in memory your old array.
so basically it depends on what you want to do with the data inside of your array.
References
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
examples:
var a = [1, 2, 3, 4];
var b = [1, 2, 3, 4];
//multiply each item for 2, it will stay on the same array.
a.forEach(i => {
i = i * 2
})
//multiply the items of B for 2 but it will return a new array
var c = b.map(i => {
return i * 2
})
console.log(a); //modified reference
console.log(b); //stays the same
console.log(c); //new array

Are native map, filter, etc. methods optimized to operate on a single intermediary array when possible?

Consider the below snippet, which converts an array of objects to an array of numbers, with negative values filtered out, and then doubled by 2:
var objects = (new Array(400)).fill({
value: Math.random() * 10 - 5
});
var positiveObjectValuesDoubled = objects.map(
item => item.value
).filter(
value => value > 0
).map(
value => value * 2
);
When chained together like this, how many actual Array objects are created in total? 1, or 3? (excluding the initial objects array).
In particular, I'm talking about the intermediary Array objects created by filter, and then by the second map call in the chain: considering these array objects are not explicitly referenced per se, are Javascript runtimes smart enough to optimize where possible in this case, to use the same memory area?
If this cannot be answered with a clear yes-or-no, how could I determine this in various browsers? (to the best of my knowledge, the array constructor can no longer be overridden, so that's not an option)
Good commentary so far, here's a summary answer: an engine might optimize for memory usage across chained method calls, but you should never count on an engine to do optimization for you.
As your example of chained methods is evaluated, the engine's memory heap is affected in the same order, step by step (MDN documentation on the event loop). But, how this works can depend on the engine...for some Array.map() might create a new array and garbage collect the old one before the next message executes, it might leave the old one hanging around until the space is needed again, it might change an array in place, whatever. The rabbithole for understanding this is very deep.
Can you test it? Sometimes! jQuery or javascript to find memory usage of page, this Google documentation are good places to start. Or you can just look at speed with something like http://jsperf.com/ which might give you at least an idea of how space-expensive something might be. But you could also use that time doing straightforward optimization in your own code. Probably a better call.

jQuery equivalent to Prototype collect and map functions?

Does jQuery offer an equivalent iterator function to "collect" and "map" in Prototype? These functions return the result of applying an iterator to each element: http://www.prototypejs.org/api/enumerable/collect
Thanks!
There's a "map()" but no "reduce()" or "collect()". The jQuery people have a considerable history of being resistant to adding a "reduce()" in the absence of clear benefit to the jQuery core code itself.
You can pick up and extend simple implementations of functions like that from the Functional.js library.
Also, be warned that the jQuery "map" facility has a couple of questionable features that are handy at times but a serious pain at others. Specifically, the callback function passed in to "map()" returns a value for the result array, as you might expect. However there are two special cases:
If the function returns null or undefined, then nothing is added to the result array; in other words, it's interpreted as a request to skip that element. This makes it hard to have a result array with explicitly null entries.
If the function returns an array, then the array is interpolated into the resulting array. In other words, if the function returns an array of 10 items, then the result array (that is, the array to be returned eventually from the call to "map()") will be augmented by 10 new elements, instead of by one element whose value is the array of 10 things.
Both those treats are handy sometimes but after spending half a day discovering the second one (yes, it's documented) I've got some painful memories :-)
The jQuery equivalent is .map() (see docs)
So if in Prototype you have:
['Hitch', "Hiker's", 'Guide', 'To', 'The', 'Galaxy'].collect(function(s) {
return s.charAt(0).toUpperCase();
}).join(''); // -> 'HHGTTG'
Simply replace collect with map (or if already using map you don't need to change anything!):
['Hitch', "Hiker's", 'Guide', 'To', 'The', 'Galaxy'].map(function(s) {
return s.charAt(0).toUpperCase();
}).join(''); // -> 'HHGTTG'

Categories