Assign methods to object by iterating over an array - javascript

In ES5, I know that it's possible to assign methods to an object using a forEach loop in the following way:
var myMethods = [
{
name: 'start',
src: someFn
},
{
name: 'stop',
src: someOtherFn
}
];
var myObject = {};
myMethods.forEach(function(method) {
myObject[method.name] = method.src;
});
In ES2015 (or ES6), is it possible to define these methods in tandem with creating the object? Here is an example of how I might expect this to work:
// example
const myObject = {
[...myMethods.map((method) => method.name)]: [...myMethods.map(method) => method.src)]
}
The end result would look like this:
const myObject = {
start: someFn,
stop: someOtherFn
}
If there is a way to iterate over these methods and assign them to myObject, I would happily restructure the myMethods array so that this is possible.
The end goal is to be able to assign each of these methods in an external module and not have to duplicate the definition.

Yes, you can use Object.assign and the spread operator in conjunction with computed property names to do
var myObject = Object.assign({}, ...myMethods.map(({name, src}) => ({[name]: src})));
First we map myMethods to an array of little one-property objects, whose key is given by the value of the name property and value by the src property. Then we use the spread operator ... to pass these to Object.assign as parameters. Object.assign then glues them all together for us.

Reduce should do the trick for you. Note that the optional second parameter is used to start with an empty object at the beginning.
var myMethods = [{
name: 'start',
src: function() {
console.log('started')
}
}, {
name: 'stop',
src: function() {
console.log('stopped')
}
}];
var myObject = myMethods.reduce((obj, method) => {
obj[method.name] = method.src;
return obj;
}, {})
console.log(myObject)
myObject.start()
myObject.stop()

Try assigning to myObject at same line of myMethods assignnemts
var myObject = {};
someFn = function(){console.log(this)};
someOtherFn = function(){console.log(this)};
var myObject = {};
someFn = function(){};
someOtherFn = function(){}
var myMethods = [
{
name: (myObject["start"] = "start"),
src: (myObject["start"] = someFn)
},
{
name: (myObject["stop"] = "stop"),
src: (myObject["stop"] = someOtherFn)
}
];

Related

Set same function to multiple objects in javascript

I have a list of objects like this
var obj = [
{ name: "user", per: { pu: [{ end: "foo" }], ge: [{ end: "bar" }] } },
{ name: "user2", per: { pu: [{ end: "foo2" }], ge: [{ end: "bar2" }] } }
];
I want to add a new property cond which is a function to the objects in pu and ge, but when i do this, the function set to the only last object.
I loop through them then set them like so obj[0].per[itm][0].cond = func and that set to the last object only, but when i try to convert function toString() it set to all, JSON.stringfy() works as func same behavior.
Have I clone or set it in another way?
You may need to post fuller code as it's difficult to see where your error is. However, considering it from scratch, I think some nested loops to match the nested arrays will get you there.
for (let o of obj) {
for (let puObj of o.per.pu) {
puObj.cond = func;
}
for (let geObj of o.per.ge) {
geObj.cond = func;
}
}
use the below function...
const updateObject = (oldObject, updatedProperties) => {
return {
...oldObject,
...updatedProperties
}
};
then you can pass in your new function or anything you need to add like this:
obj[index].per = updateObject(obj[index].per, {cond : function() {}} ); //use a loop to add the function to all objects in array
//check your current obj
console.log(obj);

can you get var name from object with nested objects with lodash?

https://jsfiddle.net/adamchenwei/Lyg2jy61/7/
RESOLVED VERSION:
https://jsfiddle.net/adamchenwei/Lyg2jy61/10/
For some reason findKey for parent top level got undefined
My objective is to get the result = 'myName' with lodash;
var obj = {
myName: {
Adam: 'Man',
},
herName: {
Eve: 'Woman',
},
};
var result = _.findKey(obj, '0'); //as you can see somehow its undefined!
var result2 = _.findKey(obj.myName, '0');
console.log(result);//objective is to get the result = 'myName' with lodash;
console.log(result2);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.4/lodash.min.js"></script>
It looks like what you're actually trying to do is get the first key of an object. To get the keys of an object, you can use _.keys. Then just retrieve the first one.
var obj = {
myName: {
Adam: 'Man',
},
herName: {
Eve: 'Woman',
},
};
console.log(_.keys(obj)[0]);
// Or if you want to do it exclusively with lodash functions
console.log(_.first(_.keys(obj)));
// or
console.log(_.head(_.keys(obj)));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.4/lodash.min.js"></script>
It's not clear what you're after exactly, but using _.findKey on the object you have posted would work like this:
https://jsfiddle.net/Lyg2jy61/8/
var obj = {
myName: {
Adam: 'Man',
},
herName: {
Eve: 'Woman',
}
};
console.log(_.findKey(obj, function(o) { return !!o.Adam; }));
If you just want the first key: _.keys(obj)[0]

Return object with subset of its attributes

I've got a flat JavaScript object like this:
{
id: 3726492,
kind: 'user',
permalink: 'nicholas',
username: 'Nicholas',
...
a lot more attributes
}
I'd like to create a new object which only has a subset of the attributes of the original object.
Something like
var newObject = oldObject.fields(['id', 'username']);
newObject would be
{
id: 3726492,
username: 'Nicholas'
}
Is there already something like this?
Try this
function pick(data, keys) {
var result = {};
keys.forEach(function (key) {
if (data.hasOwnProperty(key)) {
result[key] = data[key];
}
});
return result;
}
var data = {
id: 3726492,
kind: 'user',
permalink: 'nicholas',
username: 'Nicholas'
}
var newData = pick(data, ['id', 'kind']);
console.log(newData);
In underscorejs or lodash there is method .pick
var data = {
id: 3726492,
kind: 'user',
permalink: 'nicholas',
username: 'Nicholas',
};
var newObject = _.pick(data, 'id', 'username');
console.log(newObject);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script>
You can use Array.prototype.reduce to reduce one object to another using the list of properties:
function subset(obj, propList) {
return propList.reduce(function(newObj, prop) {
obj.hasOwnProperty(prop) && (newObj[prop] = obj[prop]);
return newObj;
}, {});
}
var obj = {
id: 3726492,
kind: 'user',
permalink: 'nicholas',
username: 'Nicholas'
};
var newObj = subset(obj, ['id', 'username']);
console.log(newObj);
document.getElementById('json').innerText = JSON.stringify(newObj);
<pre id="json"></pre>
Not built-in, but you can sure define a simple function that does the job:
var original = {a:1112, b:434, c:666, d:222};
function fieldSubset(obj, fields) {
var subsetClone = {};
for( var i=0,l=fields.length; i<l; i++) {
// This can prevent filling undefined as properties
if(obj.hasOwnProperty(fields[i])) {
subsetClone[fields[i]] = obj[fields[i]];
}
}
return subsetClone;
}
fieldSubset(original, ["a", "c"]);
You can also use this in Object.prototype, but be aware that this might happen to conflict with native API in the future versions of JavaScript:
var original = {a:1112, b:434, c:666, d:222};
Object.defineProperty(Object.prototype, "fieldSubset", {
value: function(fields) {
var subsetClone = {};
for( var i=0,l=fields.length; i<l; i++) {
// This can prevent filling undefined as properties
if(this.hasOwnProperty(fields[i])) {
subsetClone[fields[i]] = this[fields[i]];
}
}
return subsetClone;
},
enumerable: false,
configurable: true}
);
original.fieldSubset(["a", "c"]);
One liner using Array.prototype.reduce. We are also using Object.assign. The idea is to keep extending a blank object with the keys found in the filters array. If you see, the reduce function takes a callback function with arg1,arg2,arg3 params as the first argument and an empty object as the second argument. This object will be cloned and extended with the help of the keys specified in the filters array.
var a = {
id: 3726492,
kind: 'user',
permalink: 'nicholas',
username: 'Nicholas',
};
var filters = ["id","username","permalink"];
var sub = Object.keys(a).reduce((arg1,arg2,arg3)=>{ var res = {}; if(filters.indexOf(arg2)>=0){ res[arg2] = a[arg2]; } return Object.assign(arg1,res);},{})
console.log(sub);
You haven't specifically mentioned what is the type of values behind your object's keys. Your current answers cover the shallow copy and deep copy.
Another alternative would be to create a view of the original object. This would be helpful if you have very large data objects and you do not want them copy in the memory.
function View(obj, properties) {
var view = {};
properties.forEach(function(prop) {
Object.defineProperty(view, prop, {
get: function() {
return obj[prop];
},
set: function(val) {
obj[prop] = val;
},
enumerable: true,
configurable: true
});
});
return view;
}
then with your data you can do:
var data = {
id: 3726492,
kind: 'user',
permalink: 'nicholas',
username: 'Nicholas',
},
view = new View(data, ['id', 'username']);
view.id; // 3736492
view.username; // Nicholas
of course you have to be aware that you can change your original object just by view.id = 'something else'. However it is easily preventable.

How can I get console.log to output the getter result instead of the string "[Getter/Setter]"?

In this code:
function Cls() {
this._id = 0;
Object.defineProperty(this, 'id', {
get: function() {
return this._id;
},
set: function(id) {
this._id = id;
},
enumerable: true
});
};
var obj = new Cls();
obj.id = 123;
console.log(obj);
console.log(obj.id);
I would like to get { _id: 123, id: 123 }
but instead I get { _id: 123, id: [Getter/Setter] }
Is there a way to have the getter value be used by the console.log function?
You can use console.log(Object.assign({}, obj));
Use console.log(JSON.stringify(obj));
Since Nodejs v11.5.0 you can set getters: true in the util.inspect options. See here for docs.
getters <boolean> | <string> If set to true, getters are inspected. If set to 'get', only getters without a corresponding setter are inspected. If set to 'set', only getters with a corresponding setter are inspected. This might cause side effects depending on the getter function. Default: false.
You can define an inspect method on your object, and export the properties you are interested in. See docs here: https://nodejs.org/api/util.html#util_custom_inspection_functions_on_objects
I guess it would look something like:
function Cls() {
this._id = 0;
Object.defineProperty(this, 'id', {
get: function() {
return this._id;
},
set: function(id) {
this._id = id;
},
enumerable: true
});
};
Cls.prototype.inspect = function(depth, options) {
return `{ 'id': ${this._id} }`
}
var obj = new Cls();
obj.id = 123;
console.log(obj);
console.log(obj.id);
I needed a pretty printed object without the getters and setters yet plain JSON produced garbage. For me as the JSON string was just too long after feeding JSON.stringify() a particularly big and nested object. I wanted it to look like and behave like a plain stringified object in the console. So I just parsed it again:
JSON.parse(JSON.stringify(largeObject))
There. If you have a simpler method, let me know.
On Node.js, I suggest using util.inspect.custom, which will allow you to pretty print getters as values, while keeping other properties output unchanged.
It will apply to your specific object only and won't mess the general console.log output.
The main benefit vs Object.assign is that it happens on your object, so you keep the regular generic console.log(object) syntax. You don't have to wrap it with console.log(Object.assign({}, object)).
Add the following method to your object:
[util.inspect.custom](depth, options) {
const getters = Object.keys(this);
/*
for getters set on prototype, use instead:
const prototype = Object.getPrototypeOf(this);
const getters = Object.keys(prototype);
*/
const properties = getters.map((getter) => [getter, this[getter]]);
const defined = properties.filter(([, value]) => value !== undefined);
const plain = Object.fromEntries(defined);
const object = Object.create(this, Object.getOwnPropertyDescriptors(plain));
// disable custom after the object has been processed once to avoid infinite looping
Object.defineProperty(object, util.inspect.custom, {});
return util.inspect(object, {
...options,
depth: options.depth === null ? null : options.depth - 1,
});
}
Here is a working example in your context:
const util = require('util');
function Cls() {
this._id = 0;
Object.defineProperty(this, 'id', {
get: function() {
return this._id;
},
set: function(id) {
this._id = id;
},
enumerable: true
});
this[util.inspect.custom] = function(depth, options) {
const getters = Object.keys(this);
/*
for getters set on prototype, use instead:
const prototype = Object.getPrototypeOf(this);
const getters = Object.keys(prototype);
*/
const properties = getters.map((getter) => [getter, this[getter]]);
const defined = properties.filter(([, value]) => value !== undefined);
const plain = Object.fromEntries(defined);
const object = Object.create(this, Object.getOwnPropertyDescriptors(plain));
// disable custom after the object has been processed once to avoid infinite looping
Object.defineProperty(object, util.inspect.custom, {});
return util.inspect(object, {
...options,
depth: options.depth === null ? null : options.depth - 1,
});
}
};
var obj = new Cls();
obj.id = 123;
console.log(obj);
console.log(obj.id);
Output:
Cls { _id: 123, id: 123 }
123
Use spread operator:
console.log({ ... obj });

Extend a JavaScript object

I'm looping inputs in a table, that don't have any form tag. I get the values correctly. I want to build with their values an object that contains multiple objects.
What i'm expecting?
alarms = { alarm: { status_id: '1', alarm_name: 'Critic', user_id_created: '30021061' }, alarm: { status_id: '1', alarm_name: 'Middle', user_id_created: '30021061' }, alarm: { status_id: '1', alarm_name: 'Bottom', user_id_created: '30021061' }, ... };
What i'm getting? The last object in the loop.
alarms = { alarm: { status_id: '1', alarm_name: 'Bottom', user_id_created: '30021061' } };
Here is the code:
var alarms = {}
$('.new_alarm').each(function() {
var status_id = $(this).children('.status').children().val(),
alarm_name = $(this).children('.data').children('input[name="alarm_name"]').val(),
user_id = $('#user_id').text();
objAux = {};
if(alarm_name) {
objAux = {
alarm: {
'status_id': status_id,
'alarm_name': alarm_name,
'user_id_created': user_id
}
};
}
alarms = $.extend(true, alarms, objAux);
});
What's wrong with the jQuery extend method? Why is not merging the objects?
If I'm not mistaken, what you want is actually impossible. It's akin to saying you want an array to have 5 values for the a[1].
You could implement this using an array instead of an object:
alarms = [{...},{...},{...}];
What you're writing is actually this:
alarms['alarm'] = {...};
alarms['alarm'] = {...};
alarms['alarm'] = {...};
alarms['alarm'] = {...};
You're overriding the same property "alarm" on every iteration.
You should be creating an Array and then push() the values on the end of an Array.
var alarms = [
{name: 'alarm1'}
, {name: 'alarm2'}
, {name: 'alarm3'}
];
var new_alarms = [];
$(alarms).each(function() {
console.log(this);
new_alarms.push(this);
});
console.log(alarms, new_alarms);
See: http://jsfiddle.net/y22Hk/

Categories

Resources