Changing an array without chaining the reference - javascript

Maybe Iā€™m going about this all the wrong way, if so, please let me know!
Anyways, say I have an Array representing some nice data.
var orignal = ['a', 'b'];
var copy = orignal;
orignal.push('c');
console.log(original === copy); // true
I kind of like this functionality where the to objects still are the same. Of course if I would set original = ['c'] that would not be the case anymore. Is there any pattern to let me do more things on the array without breaking the link?

Assignment is the one thing that will break the link, because it is an operation on the reference, not the array. It makes the variable refer somewhere else. Anything else you do will be visible through both original and copy:
copy.length = 5; // visible through original
copy[3] = 4; // visible through original
copy.pop(); // visible through original
If you think you want assignment to preserve the link, you should probably instead be doing some similar but distinct operation, such as filling an array with the contents of another.
Incidentally, it's probably a bad idea to call the variable copy, since it's not a copy. It's the original, just like original is.

You should do inheritance :
function inherits(base, extension)
{
for ( var property in base )
{
try
{
extension[property] = base[property];
}
catch( warning ){}
}
}
and then :
var original = ['a', 'b'];
var copy=[]; inherits(original,copy) //NOT : var copy = orignal;
original.push('c');
console.log(original === copy); // false
DEMO : http://jsfiddle.net/abdennour/e64vB/1/

Related

JS (and maybe React): Is this "find" method actually returning a reference to the array value?

I'm currently reading this somewhat outdated but fine React tutorial, and I spent hours looking at this little piece of trouble, which might be more Javascript-related than React-related. At a certain point, the author starts building a small notepad app, and then there's this code:
var onChangeNote = function (id, value) {
var note = _.find(notepad.notes, function (note) {
return note.id === id;
});
if (note) {
note.content = value;
}
onChange();
};
In the app, which can be viewed in full at the forementioned article or on the respective fiddle, we have a list of notes (which by itself is an array assigned to the notes property on a notepad object defined at the top of the script), and the selected one may be changed by the user, all while using React.
What really gets me is that this is the function responsible for changing the content of the note, in the note.content = value; line, but note is a variable that got its value from _.find() (it's the lodash variant, but I already tried replacing it with the vanilla JS array.find() and nothing changed), and yet, changing it appears to be updating the actual array, I found nowhere in the code any other instance of the selected note being changed, and the onChange() function just updates the view layer (therefore it doesn't do anything to the notepad itself), so this has to be it. So is the variable note referencing the actual respective item on the notepad.notes array it got its value from even though Javascript doesn't usually do that?
Maybe I'm missing something really obvious, but I cannot put my finger on it.
Basing from the source we can check that _.find doesn't create a deep copy of the object, it returns the object from the array.
Taken from: https://github.com/lodash/lodash/blob/4.6.0-npm-packages/lodash.find/index.js
function createFind(findIndexFunc) {
return function(collection, predicate, fromIndex) {
var iterable = Object(collection);
if (!isArrayLike(collection)) {
var iteratee = baseIteratee(predicate, 3);
collection = keys(collection);
predicate = function(key) { return iteratee(iterable[key], key, iterable); };
}
var index = findIndexFunc(collection, predicate, fromIndex);
return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined;
};
}
So yes, it returns the object "reference", and not a clone, so changing a property in it, changes the one in the array.
============
Here's an example regarding your question if javascript is pass by value or reference. Javascript is always pass by value except if the value passed is an object or array. Changing the value of a property to the object will also affect the original one. But changing the whole object will not affect the original one.
var arr = [{a: 1}, {a: 2}];
var x = arr.find(v => v.a === 1);
x.a = 5;
console.log(arr); // you'll see a is 5 here
x = 100; // we changed variable directly (note that x is the object that we extracted from the find function)
console.log(arr); // it's not changed, 5 is still the value
x = arr.find(v => v.a === 5); // let's get that object again
x = {a: 10}; // we replaced it with another object with same property but another value
console.log(arr); // still not changed

Storing a pointer in javascript

Is it possible to keep an object reference without using an holder object in javascript?
Currently when an object gets overridden I sometimes lose the reference to the "current" object state illustrated in the snippet below;
Is there a way to put a "pointer" in an array or not?
EDIT
To the questions asked:
What I have in the objects I have are references to form fields. Some of these are text fields, some of them are textareas, some of them checkboxes.
I wish to keep a map next to the direct referene of what type they are.
basicaly it would be
obj {
this.text1 = createTextField();
this.text1.datepicker();
this.text2 = createTextField();
this.area1 = createArea();
this.check = createCheck();
this.datefields = [this.text1];
this.checkboxes = [this.check];
}
So I can use the datefields/checkboxes array as a checkpoint to validate against which type a field is/should behave.
Currently I use
function datefields() { return [this.text1]; };
But I'd like to know if there's a better way to do this than to intantiate a new array when I need to check it.
I know there is a way with observers to mimic pointer behaviour, and i've fiddled with those and have some good results with that, i'm just curious if there are other ways i'm not aware of.
function myObject() {
this.myvalue = null;
this.arr = [this.myvalue];
}
myObject.prototype.alter = function() {
this.myvalue = "hello";
}
var x = new myObject();
var elem = document.getElementById('results');
function log(message) {
elem.appendChild(document.createTextNode(message));
elem.appendChild(document.createElement('br'));
}
log("x.myvalue = "+x.myvalue);
log("x.arr[0] = "+x.arr[0]);
log("calling alter");
x.alter();
log("x.myvalue = "+x.myvalue);
log("x.arr[0] = "+x.arr[0]);
<div id="results"></div>
Simple answer: Only objects (including all subtypes) are passed by reference in JS. All other simple values are copied.
For a bit more detail I would recommend reading You Don't Know JS: Types & Grammer but specifically the section Value vs Reference in Chapter 2:
In JavaScript, there are no pointers, and references work a bit differently. You cannot have a reference from one JS variable to another variable. That's just not possible.
Quoting further on:
Simple values (aka scalar primitives) are always assigned/passed by value-copy: null, undefined, string, number, boolean, and ES6's symbol.
Compound values -- objects (including arrays, and all boxed object wrappers -- see Chapter 3) and functions -- always create a copy of the reference on assignment or passing.
There are plenty of examples included to show these points. I would highly recommend reading through to get a better understanding of how values/references work in JS.
There is no pointers in Javascript, though you could cheat a little using a wrapper object. Here is a minimal implementation of such an object:
var Wrapper = function (value) {
this.value = value;
};
Wrapper.prototype.valueOf = function () {
return this.value;
};
Then you may use it in place of the original value:
function myObject() {
this.myvalue = new Wrapper(null); // wrapper
this.arr = [this.myvalue];
}
myObject.prototype.alter = function() {
this.myvalue.value = "hello"; // notice the ".value"
}
The rest of your code needs no tweaks.

How to detect when a property is added to a JavaScript object?

var obj = {};
obj.a = 1; // fire event, property "a" added
This question is different from this one, where ways to detect when an already declared property is changed, being discussed.
this is possible, technically, but since all current JS implementations that I know of are single threaded it won't be very elegant. The only thing I can think of is a brute force interval:
var checkObj = (function(watchObj)
{
var initialMap = {},allProps = [],prop;
for (prop in watchObj)
{
if (watchObj.hasOwnProperty(prop))
{//make tracer object: basically clone it
initialMap[prop] = watchObj[prop];
allProps.push(prop);//keep an array mapper
}
}
return function()
{
var currentProps = [];
for (prop in watchObj)
{
if (watchObj.hasOwnProperty(prop))
{//iterate the object again, compare
if (watchObj[prop] !== initialMap[prop])
{//type andvalue check!
console.log(initialMap[prop] + ' => ' watchObj[prop]);
//diff found, deal with it whichever way you see fit
}
currentProps.push(prop);
}
}
//we're not done yet!
if (currentProps.length < allProps.length)
{
console.log('some prop was deleted');
//loop through arrays to find out which one
}
};
})(someObjectToTrack);
var watchInterval = setInterval(checkObj,100);//check every .1 seconds?
That allows you to track an object to some extent, but again, it's quite a lot of work to do this 10/sec. Who knows, maybe the object changes several times in between the intervals, too.All in all, I feel as though this is a less-then-ideal approach... perhaps it would be easier to compare the string constants of the JSON.stringify'ed object, but that does mean missing out on functions, and (though I filtered them out in this example) prototype properties.
I have considered doing something similar at one point, but ended up just using my event handlers that changed the object in question to check for any changes.
Alternatively, you could also try creating a DOMElement, and attach an onchange listener to that... sadly, again, functions/methods might prove tricky to track, but at least it won't slow your script down as much as the code above will.
You could count the properties on the object and see if has changed from when you last checked:
How to efficiently count the number of keys/properties of an object in JavaScript?
this is a crude workaround, to use in case you can't find a proper support for the feature in the language.
If performance matters and you are in control of the code that changes the objects, create a control class that modifies your objects for you, e.g.
var myObj = new ObjectController({});
myObj.set('field', {});
myObj.set('field.arr', [{hello: true}]);
myObj.set('field.arr.0.hello', false);
var obj = myObj.get('field'); // obj === {field: {arr: [{hello: false}]}}
In your set() method, you now have the ability to see where every change occurs in a pretty high-performance fashion, compared with setting an interval and doing regular scans to check for changes.
I do something similar but highly optimised in ForerunnerDB. When you do CRUD operations on the database, change events are fired for specific field paths, allowing data-bound views to be updated when their underlying data changes.

javascript: Only return if not false

Scenario: I'm searching for a specific object in a deep object. I'm using a recursive function that goes through the children and asks them if I'm searching for them or if I'm searching for their children or grandchildren and so on. When found, the found obj will be returned, else false. Basically this:
obj.find = function (match_id) {
if (this.id == match_id) return this;
for (var i = 0; i < this.length; i++) {
var result = this[i].find(match_id);
if (result !== false) return result;
};
return false;
}ā€‹
i'm wondering, is there something simpler than this?:
var result = this[i].find(match_id);
if (result) return result;
It annoys me to store the result in a variable (on each level!), i just want to check if it's not false and return the result. I also considered the following, but dislike it even more for obvious reasons.
if (this[i].find(match_id)) return this[i].find(match_id);
Btw I'm also wondering, is this approach even "recursive"? it isn't really calling itself that much...
Thank you very much.
[edit]
There is another possibility by using another function check_find (which just returns only true if found) in the if statement. In some really complicated cases (e.g. where you don't just find the object, but also alter it) this might be the best approach. Or am I wrong? D:
Although the solution you have is probably "best" as far as search algorithms go, and I wouldn't necessarily suggest changing it (or I would change it to use a map instead of an algorithm), the question is interesting to me, especially relating to the functional properties of the JavaScript language, and I would like to provide some thoughts.
Method 1
The following should work without having to explicitly declare variables within a function, although they are used as function arguments instead. It's also quite succinct, although a little terse.
var map = Function.prototype.call.bind(Array.prototype.map);
obj.find = function find(match_id) {
return this.id == match_id ? this : map(this, function(u) {
return find.call(u, match_id);
}).filter(function(u) { return u; })[0];
};ā€‹
How it works:
We test to see if this.id == match_id, if so, return this.
We use map (via Array.prototype.map) to convert this to an array of "found items", which are found using the recursive call to the find method. (Supposedly, one of these recursive calls will return our answer. The ones which don't result in an answer will return undefined.)
We filter the "found items" array so that any undefined results in the array are removed.
We return the first item in the array, and call it quits.
If there is no first item in the array, undefined will be returned.
Method 2
Another attempt to solve this problem could look like this:
var concat = Function.prototype.call.bind(Array.prototype.concat),
map = Function.prototype.call.bind(Array.prototype.map);
obj.find = function find(match_id) {
return (function buildObjArray(o) {
return concat([ o ], map(o, buildObjArray));
})(this).filter(function(u) { return u.id == match_id })[0];
};
How it works:
buildObjArray builds a single, big, 1-dimensional array containing obj and all of obj's children.
Then we filter based on the criteria that an object in the array must have an id of match_id.
We return the first match.
Both Method 1 and Method 2, while interesting, have the performance disadvantage that they will continue to search even after they've found a matching id. They don't realize they have what they need until the end of the search, and this is not very efficient.
Method 3
It is certainly possible to improve the efficiency, and now I think this one really gets close to what you were interested in.
var forEach = Function.prototype.call.bind(Array.prototype.forEach);
obj.find = function(match_id) {
try {
(function find(obj) {
if(obj.id == match_id) throw this;
forEach(obj, find);
})(obj);
} catch(found) {
return found;
}
};ā€‹
How it works:
We wrap the whole find function in a try/catch block so that once an item is found, we can throw and stop execution.
We create an internal find function (IIFE) inside the try which we reference to make recursive calls.
If this.id == match_id, we throw this, stopping our search algorithm.
If it doesn't match, we recursively call find on each child.
If it did match, the throw is caught by our catch block, and the found object is returned.
Since this algorithm is able to stop execution once the object is found, it would be close in performance to yours, although it still has the overhead of the try/catch block (which on old browsers can be expensive) and forEach is slower than a typical for loop. Still these are very small performance losses.
Method 4
Finally, although this method does not fit the confines of your request, it is much, much better performance if possible in your application, and something to think about. We rely on a map of ids which maps to objects. It would look something like this:
// Declare a map object.
var map = { };
// ...
// Whenever you add a child to an object...
obj[0] = new MyObject();
// .. also store it in the map.
map[obj[0].id] = obj[0];
// ...
// Whenever you want to find the object with a specific id, refer to the map:
console.log(map[match_id]); // <- This is the "found" object.
This way, no find method is needed at all!
The performance gains in your application by using this method will be HUGE. Please seriously consider it, if at all possible.
However, be careful to remove the object from the map whenever you will no longer be referencing that object.
delete map[obj.id];
This is necessary to prevent memory leaks.
No there is no other clear way, storing the result in a variable isn't that much trouble, actually this is what variables are used for.
Yes, that approach is recursive:
you have the base case if (this.id==match_id) return this
you have the recursive step which call itself obj.find(match_id) { ... var result = this[i].find(match_id); }
I don't see any reason, why storing the variable would be bad. It's not a copy, but a reference, so it's efficient. Plus the temporary variable is the only way, that I can see right now (I may be wrong, though).
With that in mind, I don't think, that a method check_find would make very much sense (it's most probably basically the same implementation), so if you really need this check_find method, I'd implement it as
return this.find(match_id) !== false;
Whether the method is recursive is hard to say.
Basically, I'd say yes, as the implementations of 'find' are all the same for every object, so it's pretty much the same as
function find(obj, match_id) {
if (obj.id == match_id) return obj;
for (var i = 0; i < obj.length; ++i) {
var result = find(obj[i], match_id);
if (result !== false) return result;
}
}
which is definitely recursive (the function calls itself).
However, if you'd do
onesingleobjectinmydeepobject.find = function(x) { return this; }
I'm not quite sure, if you still would call this recursive.

Javascript: multi-dimensional object

I want to create an object, starting from something like:
var map = {};
Then, I want to add items with this function:
add = function(integerA, objectB) {
map[objectB.type][integerA] = objectB;
}
So, this is a random example of the object structure I want to achieve:
map = {
'SomeType' : { 0 : 'obj', 2 : 'obj', 3 : 'obj' },
'OtherType' : { 0 : 'obj', 5 : 'obj' },
};
Now, my problem. I can't do map[objectB.type][integerA] = objectB; because map[objectB.type] is not defined. I could solve this by checking if map[objectB.type] exists through an if-statement and create map[objectB.type] = {}; when necessary.
Otherwise I could pre-load all object types. However I would prefer not to have to do this.
My question: is there a way I can create the object 'on the fly' without having to check if the type already exists every time I want to call the add function or to pre-load all the types?
It is important that my add function is so fast as possible and that the map object is correct, because I need to read and write a lot in a small amount of time (it's an animation / game application).
No, there is no any other way to create objects on the fly. Only check for existence every time:
add = function(integerA, objectB) {
if (!map[objectB.type]) {
map[objectB.type] = {};
}
map[objectB.type][integerA] = objectB;
}
If you want to improve performance you might consider some caching technics.
You can use the boolean OR shortcut (which avoids at least an explicit if). It might not be that readable though:
var data = map[objectB.type] || (map[objectB.type] = {});
data[integerA] = objectB;
This works because an assignment actually returns the value that was assigned and an OR expression returns the first value that evaluates to true.
I don't think using an if has any impact on the performance though (actually, the way in my answer might be even "slower").
If you use the map only for lookups and you don't need to iterate over the dimensions, you could merge your dimensions into a single key. For example:
add = function(integerA, objectB) {
var key = objectB.type + '-' + integerA;
map[key] = objectB;
}

Categories