JavaScript Objects: what is this repl test looking for? - javascript

I am working on developing some objects within custom functions in JavaScript.
I wrote a piece of code like this:
function updateObject(obj) {
var obj = {
foo: 'foo',
bar: 'bar',
bizz: 'bizz',
bang: 'bang'
};
return obj;
}
console.log(updateObject());
I know this is not wrong in that it outputs an object with key:value pairs in the console, but it fails this test suite from Repl:
/* test suite */
(function testUpdateObject() {
var oldObj = {
cats: 'cats',
dogs: 'dogs',
};
var newObj = updateObject(oldObj);
if (typeof newObj !== 'object') {
console.error('ERROR: `createMyObject` must return an object');
return false
}
['foo', 'bar', 'bizz', 'bang'].forEach(function(key) {
if (!(key in newObj)) {
console.error('ERROR: `' + key + '` not in object returned by `updateObject`');
return false;
}
});
['foo', 'bar', 'bizz', 'bang'].forEach(function(key) {
if (newObj[key] !== key) {
console.error('ERROR: `' + key + '` should be "' + key + '" but was ' + newObj[key]);
return false;
}
});
if (!(newObj.cats === 'cats' && newObj.dogs === 'dogs')) {
console.error('ERROR: your function doesn\'t preserve existing key/value pairs');
return false;
}
console.log('SUCCESS: `updateObject` works correctly!');
})();
Specifically, the function fails to preserve existing key/value pairs. This is what I am trying to resolve.
I also tried it with dot notation and square bracket notation like so:
function updateObject(obj) {
var obj = {};
{
obj.foo ='foo',
obj.bar ='bar',
obj.bizz ='bizz',
obj.bang ='bang'
}
return obj;
}
and
function updateObject(obj) {
var obj = {};
{
obj['foo'] ='foo';
obj['bar'] ='bar';
obj['bizz'] ='bizz';
obj['bang'] ='bang';
}
return obj;
}
Both cases console.log(updateObject());.
I am out of ideas as to how to write this custom function with object that will pass the test where it preserves key/value pair and I am looking for some guidance on how to figure this out.
I have been unable to find a Stack Overflow article that speaks directly to this case, nor have I found any documentation online that speaks to how to write a function that preserves existing key/value pairs.

You never update the object, because you assign a new object to the given variable/parameter. You need to keep the array and assign the values step by step, or use, with ES6 Object.assign.
function updateObject(obj) {
obj = obj || {};
obj.foo = 'foo';
obj.bar = 'bar';
obj.bizz = 'bizz';
obj.bang = 'bang';
return obj;
}
console.log(updateObject());
(function testUpdateObject() {
var oldObj = {
cats: 'cats',
dogs: 'dogs',
};
var newObj = updateObject(oldObj);
if (typeof newObj !== 'object') {
console.error('ERROR: `createMyObject` must return an object');
return false
}
['foo', 'bar', 'bizz', 'bang'].forEach(function (key) {
if (!(key in newObj)) {
console.error('ERROR: `' + key + '` not in object returned by `updateObject`');
return false;
}
});
['foo', 'bar', 'bizz', 'bang'].forEach(function (key) {
if (newObj[key] !== key) {
console.error('ERROR: `' + key + '` should be "' + key + '" but was ' + newObj[key]);
return false;
}
});
if (!(newObj.cats === 'cats' && newObj.dogs === 'dogs')) {
console.error('ERROR: your function doesn\'t preserve existing key/value pairs');
return false;
}
console.log('SUCCESS: `updateObject` works correctly!');
})();

Just don't overwrite your parameter ...
function updateObject(obj) {
obj['foo'] ='foo';
obj['bar'] ='bar';
obj['bizz'] ='bizz';
obj['bang'] ='bang';
return obj;
}

you're redefining obj when you say "var obj". remove the var. Updated code below.
function updateObject(obj) {
var newObj = {
foo: 'foo',
bar: 'bar',
bizz: 'bizz',
bang: 'bang'
};
// Apply all props of newObj to oldObj, while preserving oldObj
return Object.assign(obj, newObj);
}
be aware that changing the original object is enough if you want to update it, you technically don't have to return the updated object (reference is preserved when you pass it into the function), but it's probably good practice to anyway.

if (!(newObj.cats === 'cats' && newObj.dogs === 'dogs')) {
console.error('ERROR: your function doesn\'t preserve existing key/value pairs');
return false;
}
So the problem is you don't preserve existing key/value pairs.
Since you redefine obj with a var, instead of adding the new properties to it, you overwrite all of the old pairs.
So the correct way is editting the obj that gets inputted instead of redefining it. You could add them manually:
function updateObject(obj) {
obj.foo = 'foo';
obj.bar = 'bar';
obj.bizz = 'bizz';
obj.bang = 'bang';
return obj;
}
Or you could use the relatively new Object.assign() method to merge two objects into eachother if your browser supports it:
function updateObject(obj) {
var extension = {
foo: 'foo',
bar: 'bar',
bizz: 'bizz',
bang: 'bang'
};
return Object.assign( obj, extension );
}

Related

Execution context of the ' this ' keyword in clone function implementation

I was looking into a function that creates a copy of provided object. I understand mostly what's happening except for the line that involves this keyword. I do understand that the original design of the this keyword was meant to point to an instance of an object in class definitions if we go back to the origins of the this keyword that was borrowed from C++. But JavaScript decided to use this keyword to provide one extra feature, carrying a link to execution context. In the following example I am trying to understand why are we using this keyword. If you have any thoughts, I would really appreciate it.
function clone(obj) {
const replace = {};
let idx = 0;
const undefCache = [];
const replacer = (key, value) => {
let result;
if (value === undefined) {
result = '__undefined__';
} else if (typeof value === 'symbol' || typeof value === 'function') {
const keyIdx = `__replaced__${idx}`;
idx += 1;
replace[keyIdx] = [this, key]; // I understand mostly what's happening except for the line
result = keyIdx;
} else {
result = value;
}
return result;
};
function reviver(key, value) {
let result;
if (value === '__undefined__') {
undefCache.push([this, key]);// I understand mostly what's happening except for the line
} else if (replace[value] !== undefined) {
result = replace[value][0][key];
} else {
result = value;
}
return result;
}
const json = JSON.stringify(obj, replacer);
console.log(json);
const newObject = JSON.parse(json, reviver);
undefCache.forEach(el => {
const [o, key] = el;
o[key] = undefined;
});
return newObject;
}
const source = {
a: 2,
b: '2',
c: false,
g: [
{ a: { j: undefined }, func: () => {} },
{ a: 2, b: '2', c: false, g: [{ a: { j: undefined }, func: () => {} }] }
]
};
const targetOne = clone(source);
console.log(targetOne);
It's used to handle nested objects when doing serialization/deserialization with JSON.parse/stringify on special values.
Within the replacer/reviver functions, the this context is the current object that the serializer (stringify) or deserializer (parse) is working on.
For example, for the object below:
myObject = {
"foo": {
"bar": function () {}
},
"bar": "Different bar"
}
When it's processing the item myObject["foo"]["bar"], this inside the replacer will be a reference to myObject["foo"] with key = "bar" and value = function () {}". This is useful because without the reference, we wouldn't know whether we were processing myObject["bar"] or myObject["foo"]["bar"].
Thus when it is saved into the array, it really just saved pair = [myObject["foo"], "bar"]. Later when it's recovered, for each of these pairs, it can just do pair[0][pair[1]] to recover myObject["foo"]["bar"].
This works similarly with the reviver and undefined. Here the problem is that the reviver cannot return undefined and have the value set to undefined, so instead the code snippet remembers which keys are like this and post-processes the copy of the object to set them properly.
Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter

How Object.defineProperty getters and setters for all unknown children in object

for example, I define an object obj:
var obj = {}
Object.defineProperty(obj, "a", {
value : 37,
writable : true,
enumerable : true,
configurable : true
});
console.log(obj.a); // => 37
I want to access all unknown children getter same value, like obj.a.b.c.d.e / obj.c.d.e.f.e.... => 37
There's no way to return a number like that, because every nested property must return an object if you want to be able to access indefinitely further nested keys - the best you can do is return an object which can be coerced to a desired string, probably with a Proxy:
const handler = {
get(_, prop) {
return prop === Symbol['toPrimitive']
? () => '37'
: objProx;
}
}
const objProx = new Proxy({}, handler);
console.log('' + objProx.a); // => '37'
console.log('' + objProx.a.b); // => '37'
console.log('' + objProx.a.b.c.d.e.foobarbaz); // => '37'
You could create a function which takes in another function as a parameter. And execute the function within a try...catch and return the value if it exists or return a default value if there is an error:
const DEFAULT_VAL = 37;
// get a function and an optional default value to override
function getAnyDepth(func, def) {
try {
return func();
} catch (e) {
return typeof def !== "undefined" ? def : DEFAULT_VAL;
}
}
var obj = { a: 45 }
console.log(getAnyDepth(() => obj.a))
console.log(getAnyDepth(() => obj.a.b.c.d.e))
console.log(getAnyDepth(() => obj.c.d.e, { b: 100 }))
Reference: How to avoid "Cannot read property 'name' of undefined" in JavaScript
If you mean you want a default value each time you access an undefined key in your object you an do :
_.defaults(obj, {value: 37"})
or you can go with the standard comparison :
if(typeof obj.a =='undefined'){
return 37
}
If obj.a is to equal 37, then obj.a.b would evaluate to 37.b which is undefined. However, if you would be OK if 37 would be the result of turning something to a primitive (e.g. by string concatenation), then you could create a proxy.
Demo:
// Preparation
function setDefaultProperty(obj, defProp) {
return new Proxy(obj, {
get(target, prop) {
if (prop in target || typeof prop === "symbol") return target[prop];
return target[defProp];
}
});
}
var sink = setDefaultProperty({}, "_default");
sink._default = sink;
sink.valueOf = () => 37;
// Demo
var obj = {};
obj = setDefaultProperty(obj, "_default");
obj._default = sink;
console.log("" + obj.a.b.c.e); // 37
console.log("" + obj.what.ever); // 37

Filter out object by properties (not deleting)

I have an object which looks like this:
var myObj: {
2:"None",
20:"A",
31:"A",
32:"A",
Social:"B",
Method:"None"
}
I am trying to return the object without the 'Social' and 'Method' properties.
First I tried deleting like this in a computed property, but it removes the properties from the main object:
props: ['myObj']
computed: {
filterOut: {
var myObject = this.myObj
delete myVar['Social'];
delete myVar['Method'];
return myObject
}
}
Then I tried using filter but it doesn't let me to do so:
var myObject = this.myObj.filter(key => {
return key != 'Method' || key != 'Social'
})
return myObject
TypeError: this.myObj.filter is not a function
What is the best way of below object from above object?
var myObj: {
2:"None",
20:"A",
31:"A",
32:"A"
}
Simply create a new object without those properties.
There's a stage 3 proposal for rest/spread properties that would let you do it this way:
const {Social, Method, ...newObj} = originalObj;
(Then just ignore the Social and Method constants.) But it's still just stage 3 (although it's nearing stage 4 and looks good to be in ES2018; it's supported without special flags in recent versions of both Chrome and Firefox).
Note that that will only handle own, enumerable properties.
In ES2015+ you could do it like this:
const newObj = {};
for (const key of Object.keys(originalObj)) {
if (key != "Social" && key != "Method") {
newObj[key] = originalObj[key];
}
}
Or in ES5:
var newObj = {};
Object.keys(originalObj).forEach(function(key) {
if (key != "Social" && key != "Method") {
newObj[key] = originalObj[key];
}
});
Those both also handle own, enumerable properties; if you want all own non-Symbol properties, use Object.getOwnPropertyNames instead.
Use Object.assign to create new object and then delete.
var myObj = {
2:"None",
20:"A",
31:"A",
32:"A",
Social:"B",
Method:"None"
};
var new_obj = Object.assign({},myObj); // or use spread operator {...myObj};
delete new_obj.Social;
delete new_obj.Method;
console.log(new_obj);
The function you want is reduce.
Try this something like this:
function deleteProperty(object, property) {
return Object.keys(object).reduce((obj, key) => {
obj = obj || {};
if (key !== property) {
return { ...obj, [key]: object[key]};
}
return obj;
})
}
You might need to do something like this if you're working in the browser or don't want to deal with babel. Do note the below is untested, the above is function tested and I use it almost everyday.
function deleteProperty(object, property) {
return Object.keys(object).reduce((obj, key) => {
obj = obj || {};
if (key !== property) {
return Object.assign(obj, {[key]: object[key]});
}
return obj;
})
}
Maybe this, universal solution can help also:
var myObj = {
2: "None",
20: "A",
31: "A",
32: "A",
Social: "B",
Method: "None"
}
var filtered = _.pickBy(myObj, (v,k)=> _.toInteger(k))
console.log(filtered)
<script src="https://unpkg.com/lodash"></script>

How to write a complex object in memory to a file in nodejs

I am creating a complex object dynamically with nodejs in an effort to write this data to a file for use as a frontend utility library.
Is there any way that I can read/write this object to a file?
Methods I am aware of that fall short:
JSON.stringify : need to output methods
jsDump : end up with [code] blocks for function body
writing my own 'recursive serializer'
trying to create a new Buffer from the object
Function.prototype.toString
I'm beginning to think that this is just not possible. Can anyone prove me wrong?
Note: As easy as it is to just read/concat/write files together with workable code for the browser, I would also like to be able to use this as an npm package. The library is set up in a fashion where each bit of reusable code is living in its own file. A build tool allows you to select which files you would like to include in the end result. The complex object I speak of is actually just a global namespace for all of the resulting functionality.
Edit for clarity
Resulting object stored in memory
var obj = {
foo: 'a string',
reg: /\s+/,
num: 100,
fart: function (name) {
return name + ' farted and it sounded like pffftt';
},
Fart: function (sound) {
this.sound = sound;
}
};
And then you write that object character-for-character in a file for use in a browser.
Here is a demo, which covers your test case: http://jsfiddle.net/ZFUws/.
function isArray (obj) {
return Array.isArray(obj);
}
function isObject (obj) { //detect only simple objects
return obj.__proto_ === Object.prototype;
}
function each (obj, iterator) {
for (var key in obj) if (obj.hasOwnProperty(key)) iterator(obj[key]);
}
function map (obj, iterator) {
if (Array.isArray(obj)) return obj.map(iterator);
var newObj = {};
for (var key in obj) if (obj.hasOwnProperty(key)) newObj[key] = iterator(obj[key]);
return newObj;
}
var special = {
'regexp' : {
match : function (obj) {
return obj instanceof RegExp;
},
serialize : function (obj) {
return {
$class : 'regexp',
source : obj.source,
global : obj.global,
multiline : obj.multiline,
lastIndex : obj.lastIndex,
ignoreCase : obj.ignoreCase
}
},
deserialize : function (json) {
var flags = '',
regexp;
flags += json.global ? 'g' : '';
flags += json.multiline ? 'm' : '';
flags += json.ignoreCase ? 'i' : '';
regexp = new RegExp(json.source, flags);
regexp.lastIndex = json.lastIndex
return regexp;
}
},
'function' : {
match : function (obj) {
return obj instanceof Function;
},
serialize : function (obj) {
return {
$class : 'function',
source : obj.toString()
}
},
deserialize : function (json) {
return (new Function('return ' + json.source))();
}
}
}
function toJSON (obj) {
return map(obj, function (val) {
var json;
each(special, function (desc) {
if (desc.match(val)) json = desc.serialize(val);
});
if (isArray(val) || isObject (val)) json = toJSON(val);
return json ? json : val;
});
}
function fromJSON (json) {
return map(json, function (val) {
var obj;
if (val.$class) {
obj = special[val.$class].deserialize(val);
}
if (isArray(val) || isObject (val)) obj = fromJSON(val);
return obj ? obj : val;
});
}
var obj = {
foo: 'a string',
reg: /\s+/,
num: 100,
fart: function (name) {
return name + ' farted and it sounded like pffftt';
},
Fart: function (sound) {
this.sound = sound;
}
};
var serialized = JSON.stringify(toJSON(obj)),
deserialized = fromJSON(JSON.parse(serialized));
console.log(deserialized.foo);
console.log(deserialized.num);
console.log('11 '.search(deserialized.reg));
console.log(deserialized.fart('John'));
console.log(new deserialized.Fart('pffftt').sound);

In Javascript, if there is an object with a lot of properties that are functions, how do you convert them to array of strings (of the function names)?

In Javascript, if an object has lots of properties that are functions:
var obj = { foo: function() { ... },
bar: function() { ... },
...
}
then how can you get an array of names of those functions? That is, an array
["foo", "bar", ... ]
thanks.
var names = [];
for( var k in obj ) {
if(obj.hasOwnProperty(k) && typeof obj[k] == 'function') {
names.push(k);
}
}
var functions = [];
for (var prop in obj) {
if ((typeof obj[prop]) == 'function') {
// it's a function
functions.push(prop);
}
}
Edit: I've slightly misread the question, you want to extract the names of only the properties that are function objects:
function methods(obj) {
var result = [];
for (var prop in obj) {
if (obj.hasOwnProperty(prop) && typeof obj[prop] == 'function') {
result.push(prop);
}
}
return result;
}
var obj = {
foo: function() { },
bar: function() { },
};
methods(obj); // ["foo", "bar"]
I'm using the hasOwnProperty method, to ensure that the enumerated properties in fact exist physically in the object.
Note that this approach and all other answers have a small problem IE.
The JScript's DontEnum Bug, custom properties that shadow non-enumerable properties (DontEnum) higher in the prototype chain, are not enumerated using the for-in statement, for example :
var foo = {
constructor : function() { return 0; },
toString : function() { return "1"; },
valueOf : function() { return 2; }
toLocaleString : function() { return "3"; }
};
for (var propName in foo ) { alert(propName); }
The object foo clearly has defined four own properties, but those properties exist in Object.prototype marked as DontEnum, if you try to enumerate the properties of that object with the for-in statement in IE, it won't find any.
This bug is present on all IE versions, and has been recently fixed in IE9 Platform Preview.
To complete other answers: you can also use instanceof:
var obj = { foo: function() { ... },
bar: function() { ... },
...
},
fnArr = [];
for (var label in obj){
if (obj[label] instanceof Function){
fnArr.push(label)
}
}
With ES5:
var obj = {
foo: function() {},
bar: function() {},
baz: true
};
function getMethods(object) {
return Object.keys(object).filter(function(key) {
return typeof object[key] == 'function';
});
}
getMethods(obj); // [foo, bar]
Object.keys(<object>) returns the names of all enumerable properties of an object as an array, of which the non-functions are filtered out.
Example - works on Chrome release and nightly builds of Webkit and Tracemonkey (Firefox).

Categories