I have an object in JavaScript that looks like this:
var obj = {
key: [1],
key2: [2],
key3: [3]
};
And I eventually end up copying one of these elements into the object under a different key:
obj.key4 = obj.key.slice ()
Which should create a copy of key & place it into key4. This works - after this point, the new key4 is added into the object.
However, I'm then removing the original array (under key):
delete obj.key;
And it seems that once the garbage collector runs, the values under key4 are also deleted. I've tried referencing obj.key without slicing, and that produces the same issue.
It seems that the only fix I've been able to make is to not delete obj.key, but this produces errors with my code later.
I've instead tried this as well, in place of the deletion:
obj.key = null;
But this also results in the same problem - key4 will be deleted from the object. I've tried rewriting my code & restructuring the object, but I run into the same issues. Is there something I'm missing here?
Related
Update: This code just works :) Other logic in my page caused a problem
I'm reading out a JavaScript object from a jQuery data object:
$('body').data('myvals', {var1:"lorem",var2:"ipsum",var3:"dolores",var4:"amet"});
var obj = $('body').data('myvals');
I can successfully access the contents of this object
console.log(Object.entries(obj));
This returns (in Firefox console):
[["var1", "lorem"], ["var2", "ipsum"], ["var3", "dolores"], ["var4", "amet"]]
But I don't succeed in retrieving a specific property (getting 'lorem' by accessing 'var1'). Following attempts return undefined:
console.log(obj.var1);
console.log(obj[var1]);
What am I doing wrong?
this works just fine, I don't see what's the problem: DEMO
$('body').data('myvals', {var1:"lorem",var2:"ipsum",var3:"dolores",var4:"amet"});
var obj = $('body').data('myvals');
console.log(obj.var1);
console.log(obj['var1']);
if the code above doesn't work try console.log($('body').data('myvals')); and see if it returns any value.
if it returns undefined then you've probably forgotten to use document ready, just try wrapping your code in $(function(){ ... })
You can access the entries returned as pairs from Object.entries using destructuring as below:
Object.entries(obj).forEach(([key, value]) => {
console.log(key);
console.log(value);
})
An alternative way to iterate using for-of loop
let obj = { one: 1, two: 2 };
for (let [k,v] of Object.entries(obj)) {
console.log(`${k}: ${v}`);
}
This :
[["var1", "lorem"], ["var2", "ipsum"], ["var3", "dolores"], ["var4", "amet"]]
is an array with indexes as keys.Its is not an Object (Even though in Javascript pretty much everything is an Object) So when you try obj.var1 you will get undefined.
Cause there is no obj.var1 in there. There is obj[0] which holds array("var1", "lorem"); inside it.
This :
{"var1":"lorem"}, {"var2":"ipsum"},{"var3":"dolores"},{"var4":"amet"}
is an Object , in this one if you type console.log(obj.var1) you will get "lorem".
You added more code since i replied so i knopw need to change my annswer.
Your issue is "Object.entries()" , this will return an Array , you need to use
Object.keys(obj) .forEach(function(key){
console.log(key);
});
Addition
Your question layout and provided info have been a bit confusing.
Avoiding what you wrote at the start and focusing only on your last addition to your question , your only error is the:
console.log(obj[var1]);
it should be
console.log(obj["var1"]);
Other than that it should work as expected.
Instead of printing Object.entries(obj) to the console, it would be clearer for you to print the object itself to the console. The entry list is a distraction from the issue you're dealing with. What I mean is this:
var obj = $('body').data('myvals');
console.log(obj);
//or this
console.log(JSON.stringify(obj));
That will show you the actual content of the associative array. From there, either of these should work. Don't forget to use string quotes for the array index:
console.log(obj.var1);
console.log(obj["var1"]); // obj[var1] is incorrect, need quotes
If you see "var1" as a key in the full object printout above, then individual property should also be shown here. Otherwise, some other part of your code must have made changes to the content of obj or $('body).data('myvals') by the time you extract "var1"
I found a weird JS behavior :
var myArray = [ [1] , [2] , [3] ];
var myArrayCopy = [];
myArrayCopy.push( myArray[1] );
alert( myArrayCopy ); // 2, as expected.
myArrayCopy[0][0] = 'foo';
alert( myArrayCopy ); // 'foo', as expected.
alert( myArray ); // 1, foo, 3 = WTF ? :)
See the Demo.
Note that this doesn't work if we push directly values instead of arrays.
To me, it looks like pushing arrays into an array, translates somehow as if we pushed only references to those arrays, rather than copies, (which is not the behavior one would expect, please correct if I'm wrong).
Can someone explain why ?
Aliasing.
You are putting in myArrayCopy a reference to an existent object (an array) that is contained in myArray. Because of that, when you modify that array, you are modifying the actual object referred from both the above mentioned arrays.
Please, note that when you pass around objects in JavaScript they are not copied, instead you are passing around a reference to an instance and modify it in the scope of the receiver will result in a modified object in the scope of the caller as well.
There are some noticeable exceptions to this rule (as an example primitive types for they are not objects, or strings for they actually copied on write, and so on), but let's concentrate on your problem.
Considering that an array is an object, it happens the same in the following example, but it is far clearer from my point of view:
var o = { "foo": " bar" };
myArray[0] = o;
myArrayCopy[0] = myArray[0];
o.foo = "nolongerbar";
What does it return myArrayCopy[0].foo? And what myArray[0].foo?
That's called aliasing in most of the OO languages, anyway it's a common error when you deal with objects' references.
EDIT
Oh, it is not a syncing trick in any case, it's usually the reason behind annoying bugs. :-)
The value of myArray[1] is an array ([2]). When you push that into myArrayCopy in myArrayCopy.push( myArray[1] );, you push the array, not it's contents ([2] and not 2).
When you later mutate this array (myArrayCopy[0][0] = 'foo';), it obviously has an effect everywhere this array is used.
Yes, That is make sense action, because myArray1 And myArrayCopy[0] has same memory address. You can check below debugging capture
After passing through line number 9, those has been changed at same time myArray and myArraycopy's value to 'foo'
This is perfectly right.
Since arrays acts as an object, it stores the memory address
instead of storing the values itself.
var myArray = [ [1] , [2] , [3] ];
myArray will store the address of the Array created (say addr-1).
Since the elements of the array is itself an array, it will now have three addresses corresponding to array [1], [2], [3] say addr-2, addr-3, addr-4 respectively.
so finally we have:
addr-1 = [addr-2,addr-3,addr-4]
var myArrayCopy = [];
This will store a new address say addr-5 = []:
myArrayCopy.push(myArray[1]);
since you pushed the first element of myArray to myArrayCopy. It will store the address addr-3. like thisaddr-5 = [addr-3]:
`alert( myArrayCopy ); // 2, as expected
So of course this works fine as addr-3 = [2]:
myArrayCopy[0][0] = 'foo';
here you are modifying the value of zeroth element of zeroth element of myArrayCopy, i.e you change the first element at addr-3 to foo
alert( myArrayCopy ); // 'foo', as expected
So it works as well:
alert( myArray ); // 1, foo, 3
myArray now has addr-1 = [addr-2, addr-3, addr-4], where addr-2 = [1]
, addr-3 = ["foo"] and addr-4 = [3].
I have used JSON.stringify() many times and I am aware of some issues such as (described in here):
cycles
too deep objects
too long arrays
However, I am facing incorrect stringify operation on object which is like that:
After running JSON.stringify(obj) on console, I am getting that.
"[{"$$hashKey":"object:103",
"ProductCategories": [{"Id":2,"ShopProductCategoryName":"Drink","isSelected":true}
{"Id":3,"ShopProductCategoryName":"Food","isSelected":true}]
}]"
It only stringifies ProductCategories and $$hashKey which is totally unexpected.
Solving Attempts
If I create new object from obj and stringify it, returns correct JSON.
var newObj = { // Creates new object with same properties.
AllProductCategories: obj.AllProductCategories,
Id: obj.Id,
LabelName: obj.LabelName,
Percentages: obj.Percentages,
ProductCategories: obj.ProductCategories
}
JSON.stringify(newObj); // Returns correct JSON.
I used the code to send object to web api compulsorily, but the way is not what I want, of course.
As I see,
There is no cycles.
It is not too deep. (only has depth 3)
Therefore, I cannot figure out what is wrong.
Well I suggest you create a function that clones your object without $$hashKey property that was set by angular I guess:
function cloneObj (obj) {
var cloned = JSON.parse(JSON.stringify(obj));
delete cloned.$$hashKey;
for(var key in cloned) {
if(typeof cloned[key] === 'object') {
cloned[key] = cloneObj(cloned[key]);
}
}
return cloned;
}
After you clone your object without $$hashKey, then you can stringify it without any problem.
I'm trying to build a key/value relationship for an ajax based web app. I've decided to use a pure array-based approach as iterating arrays is faster than objs (or so I'm told).
The base of the idea looks like this:
var keyVals = [
[ "key1", ["value1"] ],
[ "key2", ["value2"] ],
];
However when I iterate the array to delete/set or change a key, the event doesn't run as expected:
For example:
console.log(keyVals);
function delKeyPair(key) {
for (var i = 0; i < keyVals.length; i++) {
if (keyVals[i][0] && keyVals[i][0] === key) {
Array.prototype.splice.call(keyVals, i, 1);
return true
}
}
return false
};
delKeyPair("key1");
console.log(keyVals);
When I first console.log() the array - it shows that "key1 has already been deleted, before the function is called.
here is a fiddle, not quite sure what's going on. Any help is much appreciated.
http://jsfiddle.net/3pfj8927/
key1 has not already been deleted before calling function:
try console.log(keyVals.length);
DEMO
Output:
Length before deleting: 2
Length after deleting: 1
The console runs asynchronously. It also keeps a reference to your object.
Your code is running just fine.
When you look at the object in the console, it shows you the current way it looks, not how it was at the time of the log.
If you replace console.log(keyVals); with console.log(keyVals.slice());, you should see the proper objects, because we copy them as-is.
The key1 has not been deleted. Note you're working on a reference of the given array. If you inspect the Array it will be loaded by the reference. Because you're changing the reference in the delKeyPair function it seems that there never was a key1.
If you copy the array on delete you'll see everything works like expected.
I'm experiencing an odd behavior (maybe it isn't odd at all but just me not understanding why) with an javascript array containing some objects.
Since I'm no javascript pro, there might very well be clear explanation as to why this is happening, I just don't know it.
I have javascript that is running in a document. It makes an array of objects similar to this:
var myArray = [{"Id":"guid1","Name":"name1"},{"Id":"guid2","Name":"name2"},...];
If I print out this array at the place it was created like JSON.stringify(myArray), I get what I was expecting:
[{"Id":"guid1","Name":"name1"},{"Id":"guid2","Name":"name2"},...]
However, if I try to access this array from a child document to this document (a document in a window opened by the first document) the array isn't an array any more.
So doing JSON.stringify(parent.opener.myArray) in the child document will result in the following:
{"0":{"Id":"guid1","Name":"name1"},"1":{"Id":"guid2","Name":"name2"},...}
And this was not what I was expecting - I was expecting to get the same as I did in teh parent document.
Can anyone explain to me why this is happening and how to fix it so that the array is still an array when addressed from a child window/document?
PS. the objects aren't added to the array as stated above, they are added like this:
function objTemp()
{
this.Id = '';
this.Name = '';
};
var myArray = [];
var obj = new ObjTemp();
obj.Id = 'guid1';
obj.Name = 'name1';
myArray[myArray.length] = obj;
If that makes any difference.
Any help would be much appreciated, both for fixing my problem but also for better understanding what is going on :)
The very last line might be causing the problem, have you tried replacing myArray[myArray.length] = obj; with myArray.push(obj);? Could be that, since you're creating a new index explicitly, the Array is turned into an object... though I'm just guessing here. Could you add the code used by the child document that retrieves myArray ?
Edit
Ignore the above, since it won't make any difference. Though, without wanting to boast, I was thinking along the right lines. My idea was that, by only using proprietary array methods, the interpreter would see that as clues as to the type of myArray. The thing is: myArray is an array, as far as the parent document is concerned, but since you're passing the Array from one document to another, here's what happens:
An array is an object, complete with it's own prototype and methods. By passing it to another document, you're passing the entire Array object (value and prototype) as one object to the child document. In passing the variable between documents, you're effectively creating a copy of the variable (the only time JavaScript copies the values of a var). Since an array is an object, all of its properties (and prototype methods/properties) are copied to a 'nameless' instance of the Object object. Something along the lines of var copy = new Object(toCopy.constructor(toCopy.valueOf())); is happening... the easiest way around this, IMO, is to stringency the array withing the parent context, because there, the interpreter knows it's an array:
//parent document
function getTheArray(){ return JSON.stringify(myArray);}
//child document:
myArray = JSON.parse(parent.getTheArray());
In this example, the var is stringified in the context that still treats myArray as a true JavaScript array, so the resulting string will be what you'd expect. In passing the JSON encoded string from one document to another, it will remain unchanged and therefore the JSON.parse() will give you an exact copy of the myArray variable.
Note that this is just another wild stab in the dark, but I have given it a bit more thought, now. If I'm wrong about this, feel free to correct me... I'm always happy to learn. If this turns out to be true, let me know, too, as this will undoubtedly prove a pitfall for me sooner or later
Check out the end of this article http://www.karmagination.com/blog/2009/07/29/javascript-kung-fu-object-array-and-literals/ for an example of this behavior and explanation.
Basically it comes down to Array being a native type and each frame having its own set of natives and variables.
From the article:
// in parent window
var a = [];
var b = {};
//inside the iframe
console.log(parent.window.a); // returns array
console.log(parent.window.b); // returns object
alert(parent.window.a instanceof Array); // false
alert(parent.window.b instanceof Object); // false
alert(parent.window.a.constructor === Array); // false
alert(parent.window.b.constructor === Object); // false
Your call to JSON.stringify actually executes the following check (from the json.js source), which seems to be failing to specify it as an Array:
// Is the value an array?
if (Object.prototype.toString.apply(value) === '[object Array]') {
//stringify