Unable to change enumerable attribute inside JSON.stringify replacer function? - javascript

I am trying to serialize non enumerable properties inside the replacer function.
What is wrong here?
help please.
var obj = {x:1,y:2};
Object.defineProperty(obj,"x",{enumerable:false});
var s = JSON.stringify(obj,function(key,value){
if(this.propertyIsEnumerable(key) == false){
Object.defineProperty(this,key,{enumerable:true});
}
return value;});
//"{"y":2}"
// x remains not enumerable
I guess non enumerable properties is being filtered out before the execution of replacer function. Not sure.

If .stringify filters out non-enumerable properties, then yes, they aren't available in the callback - it has already done that filter. Why don't you just loop through all the keys of the object before you stringify, and use your logic inside to set it as enumerable if it isn't already. Then, just simply call JSON.stringify(obj);. Unfortunately as you pointed out, for in loops and jQuery's $.each does not loop over non-enumerable keys. I found that .getOwnPropertyNames should accomplish what you want - https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames
Here's an example of using it, similar to your code you provided:
http://jsfiddle.net/9Em82/
var obj = {x:1,y:2};
Object.defineProperty(obj,"x",{enumerable:false});
var all = Object.getOwnPropertyNames(obj);
for (var i = 0, j = all.length; i < j; i++) {
console.log("KEY: " + all[i]);
}

Related

Javascript - Custom arrays not working

Please check the code below -
custom_array.push(...) is working
but
custom_array[i] = "n"
is not working.
There are a couple more things which i am not sure why it is not working. Please note the comments with "?" which is not working. Please refer fiddle - http://jsfiddle.net/vc0bbm3d/
The reason that setting items using bracket syntax doesn't work for your object inheriting an array, is that it's not actually an array.
An array has special code for handling property assignment (which is what you do with the bracket syntax) when the property name is an integer. If the index is outside the current length, the length property is adjusted.
When you assign a value to a property in your object, there is no special code to handle the length. The property is just assigned as usual, and the length is never adjusted. As the object already has code for handling what's happening when you use the bracket syntax, the array that it inherts never comes into play.
For the bracket syntax to work in that way, the object has to be an actual array.
try
ar[3] = 2;
ar.length =4
That way it knows you have added something.
Here's an example of what I mean when I say create a layer around the default array object to extend functionality:
var SuperArray = function(){
this.arr = Array.prototype.slice.call(arguments);
};
//abstraction of default array functionality
SuperArray.prototype.set = function(index, value){
this.arr[index] = value;
return this;
};
SuperArray.prototype.unset = function(index){
this.arr.splice(index, 1);
return this;
};
//extension of default array functionality
SuperArray.prototype.consoleList = function(){
var arr = this.arr;
for(var i = 0, l = arr.length; i < l; i++){
console.log(arr[i]);
};
return this;
};
var extArr = new SuperArray(1,2,3);
extArr.set(2, 25); //does array[2] = 25;
extArr.unset(1); //removes array[1]
extArr.consoleList();//console.logs all items in array
extArr.set(2, 25).unset(1).consoleList();//same thing
this simple object accepts arguments and sets them directly into an array, which we then manipulate how we wish. You can then add any utility functions that you need, check for existing array functionality, etc.

write a function that gets passed an object and returns an array of the object's properties

My homework is like:
Write a "keys" function that gets passed an object and returns an array of the object's properties. Be sure to screen out the object's methods. The keys array only has names of the object's name/value pairs. Because of cross browser issues(non-support in older browsers), the Objectkeys method cannot be used. Your function should provide the same service for all the browsers.
My initial code is as below:
function keys(obj){
var key="";
var i = 0;
var array = [];
for(i = 1; i<arguments.length; i++){
for(key in arguments[i]){
if(obj.hasOwnProperty&&(!_.isArray(obj))){
obj[key]=arguments[i][key];
}
}
}
for(var j = 0; j < obj.length; j++){
for(key in obj[j]){
array[j] = obj[j];
}
}
return array;
}
I'm pretty sure that my function has a lot of problems. Could you please help me with it? Thank you!
This is the solution:
function keys(obj) {
var hasOwnProperty = Object.prototype.hasOwnProperty;
var properties = [];
for (var property in obj)
if (hasOwnProperty.call(obj, property)
&& typeof obj[property] !== "function")
properties.push(property);
return properties;
}
Line by line the above code does the following:
Create an empty array properties to hold the names of all the properties of obj.
For each property property of obj do:
If the property property belongs to obj and
If obj[property] is not a function then:
Add the property property to the properties array.
Return the properties array.
See the demo: http://jsfiddle.net/qVgVn/
There are a lot of problems with your code. The answer you need is here in the MDN though: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
That function does EXACTLY what your professor asked, and it does it cross-browser. There is what is called a poly-fill, or cross-browser implementation of object.keys listed under "Compatability". Try to sort out that code to figure out what it's doing :)
Here are some problems with your own code that I see right off the bat - it's probably not working code, I just wanted to give you some guidance of things you did incorrectly:
// Name your function something useful and descriptive.
function getKeysAsArray(obj){
// For starters, dont name a variable "array" - bad practice.
var key="",
i = 0,
results = [];
// First foor loop unnecessary, do not use arguments here
// because you already know the name of your argument.
for(key in obj){
// See if this browser supports has OwnProperty by using typeof
// which will fail gracefully, vs what u did which will stop the
// script from running
if(typeof Object.hasOwnProperty === 'function'){
// You probably shouldn't be using underscore _
if(obj.hasOwnProperty && !(obj instanceof Array)){
results.push(obj[key]);
}
}
}
return results;
}
ok here i go...
function objProps(x){
var arr=[];
for (var k in x) if(typeof x[k] !='function' && x.hasOwnProperty(k)) {arr.push(k);}
return arr;
}
this code works as expected. call it with object...
get its only keys out that are NOT Functions.

Issue finding all variables in an Object

I have an Object called Button_Objs, its purpose is to hold all my Button Objects. I made a function in my Button_Objs which iterates through each of its variables. Here is where the issue is, I have a if statement that says: if (i typeof Button){}. I do this so it only selects the Button Objects that are stored.
Here is the JSFiddle of this: http://jsfiddle.net/MichaelMitchell/vcZTR/15/
var Button_Objs = function() {
this.getButtons = function() {
var i;
for (i in this) {
if (type of i == Button) { //PROBLEM, also does not work with instanceof.
document.getElementById('console').innerHTML += ( "button: " + i + "<br />");
}
}
};
};
I also tried instanceof, but alas it will not work :(
i typeof Button is invalid syntax and type of i == Button as well.
When you use a for...in loop to iterate over an object, the loop variable will hold the name of the property, not the value. You can use the name to get the value:
if(this[i] instanceof Button)
Read more about the for...in loop (I recommend to have a look at MDN to learn about JS basics).
Apart from this, I don't see why you need a constructor function to create a container for your buttons. You could just use a plain object:
var buttons = {};
// somewhere
buttons['button' + i] = new Button(...);
and then again use the for...in loop to iterate over the object.
And if you don't actually need the names (they just seem to be some sort of enumeration), why don't you use an array?
var buttons = [];
// somewhere
buttons.push(new Button(...));
and then use a for loop to iterate over all buttons. You would not even have to test for their type.

Remove element from Javascript associative array using array value

I am trying to remove an element from a Javascript associtive array using the value to find it, but I am having trouble. I have tried splice and JQuery's grep method and neither have worked for me. This is what I currently have.
var array_path = new Array();
function bulk_upload(){
var temp_array = new Object();
for (var i = 1; i<8; i++){
temp_array[i] = $('#path' + i).val();
if(temp_array[i]!='' && temp_array[i]!=null){
array_path['path' + i] = $('#path' + i).val();
}
}
process_txt();
}
function process_txt(){
//alert(array_path.indexOf(full_path)); //returns nothing
var removed_element = array_path.splice(getKey(array_path), 1);
//array_path = $.grep(array_path, function(val) { return val != full_path; });
alert(removed_element);//return nothing, just blank alert box
}
function getKey(data) {
for (var prop in data)
return prop;
}
The way to do this is to use the delete operator.
delete array_path[getKey(array_path)]
Some Background Information
In JavaScript, almost everything descends from Object.prototype. JavaScript, being an open and dynamic language allows you to create/modify properties of objects by simple assignment. This is very similar to what an associative array -- a structure that contains keyed values.
Under the hood an array is just an object that descends from Array.prototype with numeric keys and a special property called length. The length property just returns one greater than the highest numeric property. In essence, an Array is an object with different semantics.
If you're wanting an associative array then Array is not the object you want to descend from. You would want to descend directly from Object. There are two ways to do that, you could either use the new operator or an empty object literal. The syntax for both is below:
var o = new Object();
var o = {};
The second is preferred since it's a little bit more concise.
I wrote a blog post about this a while back, have a look if you want a little bit more info.
There is no such thing in JavaScript as an "associative array" per se. The data structure which corresponds to this concept is simply a JavaScript Object.
Of course, a JavaScript Array (like essentially everything in JavaScript) is an Object, but one with additional capabilities. So you can use an Array as a key-value map, but it's really not the correct structure for that.
To remove a key from an Object, you just do something like this:
var myObj = {};
var myKey = "blah";
myObj[myKey] = 1234; // Adds or updates value for "blah" to 1234.
delete myObj[myKey]; // Removes key-value pair for "blah".
Have you tried delete hash.someKey; ?
You can give your object a remove method, or use apply or call to use another object's remove method, if defined.
function myObj(members){
for(var p in members) this[p]= members[p];
}
myObj.prototype.remove= function(val){
for(var p in this){
if(this[p]=== val) delete this[p];
}
return this;
}
myObj.prototype.toString= function(){
var A= [];;
for(var p in this){
if(this.hasOwnProperty(p)){
A.push(p+':'+this[p])
}
}
return '{'+A.join(', ')+'}';
}
var O= new myObj({a: 1, b: 10, c: 100});
alert(O)
O.remove(10);
alert(O)
I'm not psychic, so I can only guess that you wanted to accomplish something like this:
var paths = [];
function getPaths() {
for(var i = 1; i < 8; ++i) {
var value = $('#path' + i).val();
if(value) paths.push(value);
}
}
function process() {
var firstPath = paths.shift();
// do stuff
}
getPaths();
if(paths.length) process();

What causes this error with for...in after assigning Array.prototype.indexOf?

I was surprised when I was able to reproduce a bug with a minimum amount of code. Note that in this minimalist example Array.indexOf isn't being called. Also note that I've tried several different implementations of indexOf, including several from stackoverflow.com.
The bug is, when the for...in executes in IE, three alerts are displayed: "indexOf", "0", and "1". In FF, as one would expect, only two ("0", "1") appear.
<html>
<body onLoad="test();">
<script language="javascript">
var testArray = ['Foo', 'Bar'];
if(!Array.prototype.indexOf) {
Array.prototype.indexOf = function (obj, fromIndex) {
if (fromIndex == null) {
fromIndex = 0;
} else if (fromIndex < 0) {
fromIndex = Math.max(0, this.length + fromIndex);
}
for (var i = fromIndex, j = this.length; i < j; i++) {
if (this[i] === obj)
return i;
}
return -1;
};
}
function test() {
var i;
for(i in testArray) {
alert(i);
}
}
</script>
</body>
</html>
Can anyone explain this? I've already changed my code to use a while so I'm not under the gun, but this one really has me stumped. It reminds me of memory overrun errors in c.
See "for in Intrigue" on the Yahoo! User Interface blog.
The reason your code is working as expected in Firefox is because you haven't added your own indexOf method in Firefox. The for in loop iterates over all the keys in the object's prototype chain, including the indexOf method you added. Douglas Crockford suggests the following solution:
for (var p in testArray) {
if (testArray.hasOwnProperty(p)) {
alert(testArray[i]);
}
}
Alternatively, you can just filter out functions:
for (var p in testArray) {
if (typeof testArray[p] !== "function") {
alert(testArray[i]);
}
}
Also, as "nickf" points out, it is best not to use the for in loop for iterating over arrays. The for in loop is intended for iterating over the keys in an object.
Steve
for .. in is meant for looping through object properties, definitely not arrays.
Stick to the standard:
for (var i = 0, l = myArray.length; i < l; ++i) { .. }
More info at the Mozilla Developer Centre:
A for...in loop does not iterate over built-in properties. These include all built-in methods of objects, such as String's indexOf method or Object's toString method. However, the loop will iterate over all user-defined properties (including any which overwrite built-in properties).
Although it may be tempting to use this as a way to iterate over an Array, this is a bad idea. The for...in statement iterates over user-defined properties in addition to the array elements, so if you modify the array's non-integer or non-positive properties (e.g. by adding a "foo" property to it or even by adding a method or property to Array.prototype), the for...in statement will return the name of your user-defined properties in addition to the numeric indexes.

Categories