Javascript variable acts like reference instead of object - javascript

I have an issue I can't quite put my finger on.
var records = dataView.getItems();
for (var i = 0; i < records.length; i++) {
var date = records[i].RecordCreated;
records[i].RecordCreated = new Date(parseInt(date.replace('/Date(', '')));
}
records = JSON.stringify(records);
This is a small loop that takes existing data from a SlickGrid dataview and converts the /Date(###)/ format to a proper date format in order to be deserialized on the server. This works fine.
The weird issue is that the var records = dataView.getItems(); definition is acting more like a reference than an individual, unique, object. Consider the following code:
var records = dataView.getItems();
// Debug Code [A]
console.log(records);
console.log(dataView.getItems());
for (var i = 0; i < records.length; i++) {
var date = records[i].RecordCreated;
records[i].RecordCreated = new Date(parseInt(date.replace('/Date(', '')));
}
// Debug Code [B]
console.log(records);
console.log(dataView.getItems());
records = JSON.stringify(records);
The console logs at [B] are both identical, where one would expect them to be different. Meaning instead of seeing the second log with unchanged /Date(###)/ format dates, proper date objects are printing.
Even stranger is that at [A] the console logs are printing as if the loop ran asynchronously and finished prior to the console.log() command was called.
This is all wrapped in a function with some conditional statements to do different loops, nothing crazy. What I need to know is how/why the records definition is not working as per usual (a unique variable in memory, that can be edited without affecting the original object).
Solution
So the problem was that records was receiving the original object from the SlickGrid library. What I did to fix the issue was clone the object before assigning it to records.
Clone function:
function clone(obj) {
if (obj == null || typeof (obj) != 'object')
return obj;
var temp = new obj.constructor();
for (var key in obj)
temp[key] = clone(obj[key]);
return temp;
}
Usage:
var __data = dataView.getItems();
records = clone(__data);
Hope this helps anybody else viewing this question in the future.

looks like your records variable is an array being returned from dataView.getItems(). This variable IS a reference to the array object, so changing things on that object will be reflected in all the references to the object.

You should ensure that dataView.getItems() calls Array.protototype.slice on any collection it is returning

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

try catch optimization with fixed array points inside a loop - javascript

Is there a better way of doing the following. I have a JSON array that gets passed in a javascript object, the object doesn't have key value pairs so i need to manual set each point in the array and bind it to a variable.
While the example bellow is only 1 item there are 80+ that need to be set.
If i try to put in item[key][2][1] as a peramiter for a function the code fails as it trys to access the array before passing off to the try catch function. Also all the try catch's happen within a loop that can run up to 200 times per page load.
The below works for everything, but when dealing with i.e 8 or below it begins to bottleneck fast. I have also isolated that it is this checking function that bottlenecks and not other parts of the code.
var someArr = "";
for(i=0;i<data.length;i++){
var item = data[i];
for(key in item){
try{someArr = item[key][2][1]}
catch(err){}
final{someArr = checkData(someArr)}
}
}
function checkData(value){
if(!value){value = "";}
return value;
}
You could exchange your try catch block with a check if the parts of variable are set.
if (item && item[key] && item[key][2]) {
someArr = item[key][2][1];
}

Instantiating class in loop in javascript uses last value

First of all, I'm aware there are many questions about closures in JavaScript, especially when it comes to loops. I've read through many of them, but I just can't seem to figure out how to fix my own particular problem. My main experience lies with C#, C++ and some ASM and it is taking some getting used to JavaScript.
I'm trying to populate a 3-dimensional array with new instances of a class (called Tile) in some for loops. All I want to do is pass along a reference to some other class (called Group) that gets instantiated in the first loop (and also added to another array). As you might have guessed, after the loops are done, every instance of the Tile class has a reference to the same Group object, namely the last one to be created.
Apparently instead of passing a reference to the Group object, a reference to some variable local to the function is passed along, which is updated in every iteration of the loop. My assumption is that solving this problem has something to do with closures as this appears to be the case with many similar problems I've come across while looking for a solution.
I've posted some trimmed down code that exposes the core of the problem on jsFiddle:
//GW2 namespace
(function( GW2, $, undefined ) {
//GW2Tile class
GW2.Tile = function(globalSettings, kineticGroup)
{
//Private vars
var tilegroup = kineticGroup;
// console.log(tilegroup.grrr); //Shows the correct value
var settings = globalSettings;
this.Test = function(){
console.log(tilegroup.grrr);
}
this.Test2 = function(group){
console.log(group.grrr);
}
} //Class
}( window.GW2 = window.GW2 || {}, jQuery ));
var zoomGroups = [];
var tiles = [];
var settings = {};
InitArrays();
tiles[0,0,0].Test(); //What I want to work, should give 0
tiles[0,0,0].Test2(zoomGroups[0]); //How I'd work around the issue
function InitArrays(){
var i, j, k, zoomMultiplier, tile;
for(i = 0; i <= 2; i++){
zoomGroups[i] = {};
zoomGroups[i].grrr = i;
tiles[i] = [];
zoomMultiplier = Math.pow(2, i);
for(j = 0; j < zoomMultiplier; j++){
tiles[i,j] = [];
for(k = 0; k < zoomMultiplier; k++){
tile = new GW2.Tile(settings, zoomGroups[i]);
tiles[i,j,k] = tile;
}
}
}
}
Up till now when working with JavaScript, I've generally fiddled with the code a bit to make it work, but I'm tired of using work-arounds that look messy as I know there should actually be some fairly simple solution. I'm just not fond of asking for help, but this is really doing my head in. Any help is very much appreciated.
Multidimensional arrays
The problem
The first issue with your code above is how you are attempting to create multidimensional arrays.
The syntax you are using is:
tiles[0,0,0]
However, the way JavaScript will interpret this is:
tiles[0]
Accessing a multidim array
If you wish to access a multidim array you have to use:
tiles[0][0][0]
And to create a multidim array you would need to do the following:
tiles = [];
tiles[0] = [];
tiles[0][0] = [];
tiles[0][0][0] = 'value';
or:
tiles = [[['value']]];
With respect to your code
In your code you should be using:
tiles[i][j][k] = tile;
But you should also make sure that each sub array actually exists before setting it's value, otherwise you'll get undefined or illegal offset errors.
You can do this by way of:
(typeof tiles[i] === 'undefined') && (tiles[i] = []);
(typeof tiles[i][j] === 'undefined') && (tiles[i][j] = []);
tiles[i][j][k] = tile;
Obviously the above can be optimised depending on how you are traversing your loops i.e. it would be best to make sure the tiles[i] level exists as an array before stepping in to the the [j] loop, and then not worry about checking it's existence again whilst stepping j.
Other options
Depending on what your dataset is, or at least what you hope to do with the tiles array it can be worth considering using an object instead:
/// set up
tiles = {};
/// assignment
tiles[i+','+j+','+k] = 'value';
However this method is likely to be slower, although I've been proved wrong a number of times by my assumptions and differing JavaScript interpreters. This would probably be were jsPerf would be your friend.
Optimisation
One benefit of using the tiles[i][j][k] approach is that it gives you the chance to optimise your references. For example, if you were about to process a number of actions at one level of your multidimensional array, you should do this:
/// set up
var ij = tiles[i][j];
/// use in loops or elsewhere
ij[k] = 'value'
This is only of benefit if you were to access the same level more than once however.

JavaScript loop is changing the source array of data as well as the result - how come?

I am completely perplexed. I have an object containing a global "hashed" array of numbers (in objectA) that is referred in a loop that combines the numbers into a new series (in objectB).
var objectB = objectA[arrActive[0]];
for (i=1; i<arrActive.length; i++) {
var _this = arrActive[i];
for (x=0; x<objectB.length; x++) {
objectB[x][1] += objectA[_this][x][1];
}
}
What's weird is that the values in objectA, the source array, are being incremented during the loop - but why? As far as I'm aware, I'm just reading from objectA to write to objectB!
This is frustrating because every time the function is called, the numbers are further inflated!
Working example on JSFiddle is here: http://jsfiddle.net/ZbWGH/ - have I completely misunderstood the += operator? I'm sure this is a simple issue to understand.
Thanks in advance for any help!
You're putting reference to the instance objectA['ONE'] in variable called objectB - any change in that variable will indeed change the actual value.
Instead you might be interested in getting clone or "clean copy" of the array into objectB and this way it won't change the original array.
Simple function that will do this is:
function CopyArray(arr) {
var clone = [];
for (var i = 0; i < arr.length; i++) {
var subArray = [];
for (var j = 0; j < arr[i].length; j++)
subArray.push(arr[i][j]);
clone.push(subArray);
}
return clone;
}
And to use it:
var objectB = CopyArray(objectA[arrActive[0]]);
Updated jsFiddle: http://jsfiddle.net/yahavbr/ZbWGH/1/
Further more A += B is like A = A + B, so you modify objectA.
Do you know C? References/pointers in C are a good way to understand komplex variables in Javascript. "Komplex" meaning everything that is not Number, String, Boolean - everything else is "Object". Variables for the komplex types (Objects) are indeed like pointers. If you know the concepts of "call by reference" and "call by value", in Javascript it's neither, sort of: If you give objects to functions the "pointer" itself is call by value, but the value is a reference to the object (really to the area on the heap where the object is stored, even though JS programmers don't handle heap like in C/C++ it still is where stuff is stored). Example:
function fn (a) {
//changing the argument itself does NOT change the original object
a = null;
//but changing its properties does:
a.foo = 42;
}
var o = { foo:1, bar:2 };
fn(o);
So now it should become clear why you have to clone an object if you want real "call by value". This implementation was chosen for JS because otherwise every single time a function is called with a non-primitive type the heap would have to be copied over, and 99% of the time that just is not necessary. The "true" spirit of functional programming would of course be pure call by value, here we see practical life (performance and memory usage) considerations intruding upon theory :)

Determine the length of this Node-set?

Javascript for Mozilla:
while (result)
{
alert(result.childNodes[0].nodeValue);
//txt=txt+result.childNodes[0].nodeValue+"<br/>";
result=nodes.iterateNext();
}
This is intended to return a series of strings from an xml file. When I use the alert line, it alerts as expected with the proper strings in a series. The "txt" variable on the commented line is supposed to go to an innerHTML further down in the function. But Firebug keeps telling me that the result.childNodes[0] is undefined. Why would it be defined in the alert but not the next line?
I hope that's enough code to determine the problem...if not I will post more.
Thanks for help
[edit]
this is the definition of result:
var x=xmlDoc.responseXML;
var nodes=x.evaluate(path, x, null, XPathResult.ANY_TYPE, null);
var result=nodes.iterateNext();
I am retrieving XML
[edit]
okay I put the iterator in a for loop like this:
for (var i=0; i<2; i++)
{
var res=(xmlNodes.childNodes[0].nodeValue);
txt=txt+res+"<br/>";
xmlNodes=xmlQuery.iterateNext();
}
because I had a theory that once the iteration was null the loop would fail. So that's what happened--it worked until I set the loop one instance higher than the amount of nodes available. now I am trying to figure out how to get the "length" of the node-set. How do I do that?
I don't see any problems in the code you have shown so far. If childNodes[0] is undefined, then it has to be a text node or an empty node, and you should see an exception when trying to access a property such as nodeValue of childNodes[0] which is undefined. The exception will show up on alert or concatenation, or any other type of access.
This is the answer to your updated question.
now I am trying to figure out how to get the "length" of the node-set. How do I do that?
You can have the following types of node-sets returned from the evaluate function:
Iterators
Snapshots
First Nodes
I'll skip "first nodes" as that doesn't apply in this situation.
Iterators
With iterators, you only get an iterateNext() method for traversing nodes sequentially. Iterators refer to live nodes, meaning if the nodes in the document were to change while you are traversing the resultset, the iterator will become invalid.
Here's an example with using an iterator to go over each resulting node:
var results = doc.evaluate("expr", doc, null, ORDERED_SNAPSHOT, null);
var node;
while(node = results.iterateNext()) {
console.log(node);
}
If you want to use iterators, and find the number of matching results, a wrapper function that iterates through all nodes and returns them in an array might be useful.
function evaluateXPath(document, expression) {
var ANY_TYPE = XPathResult.ANY_TYPE;
var nodes = document.evaluate(expression, document, null, ANY_TYPE, null);
var results = [], node;
while(node = nodes.iterateNext()) {
results.push(node);
}
return results;
}
Get nodes as an array and loop the array:
var results = evaluateXPath(doc, "expr");
for(var i = 0; i < results.length; i++) {
console.log(results[i]);
}
Snapshots
Snapshots provide a static result of the nodes at the time of querying. Changes to the document will not affect this snapshot. Useful interfaces here will be the snapshotLength property, and snapshotItem(index) function.
Here's an example using a snapshot result:
var ORDERED_SNAPSHOT = XPathResult.ORDERED_NODE_SNAPSHOT_TYPE;
var results = doc.evaluate("expr", doc, null, ORDERED_SNAPSHOT, null);
for(var i = 0; i < results.snapshotLength; i++) {
var node = results.snapshotItem(i);
console.log(node);
}
See a working example.
It seems you are developing this for Firefox. Did you consider using E4X for this purpose? It provides a really easy interface for dealing with XML documents - for creating, manipulating, and querying.
now I am trying to figure out how to
get the "length" of the node-set. How
do I do that?
In XPath this is achieved using the standard XPath count() function.
count(someExpression)
evaluates to the number of nodes selected by someExpression.

Categories