Default property on Javascript object [duplicate] - javascript

Is there a way to set the default attribute of a Javascript object such that:
let emptyObj = {};
// do some magic
emptyObj.nonExistingAttribute // => defaultValue

Since I asked the question several years ago things have progressed nicely.
Proxies are part of ES6. The following example works in Chrome, Firefox, Safari and Edge:
let handler = {
get: function(target, name) {
return target.hasOwnProperty(name) ? target[name] : 42;
}
};
let emptyObj = {};
let p = new Proxy(emptyObj, handler);
p.answerToTheUltimateQuestionOfLife; //=> 42
Read more in Mozilla's documentation on Proxies.

Use destructuring (new in ES6)
There is great documentation by Mozila as well as a fantastic blog post that explains the syntax better than I can.
To Answer Your Question
var emptyObj = {};
const { nonExistingAttribute = defaultValue } = emptyObj;
console.log(nonExistingAttribute); // defaultValue
Going Further
Can I rename this variable? Sure!
const { nonExistingAttribute: coolerName = 15} = emptyObj;
console.log(coolerName); // 15
What about nested data? Bring it on!
var nestedData = {
name: 'Awesome Programmer',
languages: [
{
name: 'javascript',
proficiency: 4,
}
],
country: 'Canada',
};
var {name: realName, languages: [{name: languageName}]} = nestedData ;
console.log(realName); // Awesome Programmer
console.log(languageName); // javascript

There isn't a way to set this in Javascript - returning undefined for non-existent properties is a part of the core Javascript spec. See the discussion for this similar question. As I suggested there, one approach (though I can't really recommend it) would be to define a global getProperty function:
function getProperty(o, prop) {
if (o[prop] !== undefined) return o[prop];
else return "my default";
}
var o = {
foo: 1
};
getProperty(o, 'foo'); // 1
getProperty(o, 'bar'); // "my default"
But this would lead to a bunch of non-standard code that would be difficult for others to read, and it might have unintended consequences in areas where you'd expect or want an undefined value. Better to just check as you go:
var someVar = o.someVar || "my default";

my code is:
function(s){
s = {
top: s.top || 100, // default value or s.top
left: s.left || 300, // default value or s.left
}
alert(s.top)
}

The way I achieve this is with the object.assign function
const defaultProperties = { 'foo': 'bar', 'bar': 'foo' };
const overwriteProperties = { 'foo': 'foo' };
const newObj = Object.assign({}, defaultProperties, overwriteProperties);
console.log(defaultProperties); // {"foo": "bar", "bar": "foo"}
console.log(overwriteProperties); // { "foo": "foo" };
console.log(newObj); // { "foo": "foo", "bar": "foo" }

This seems to me the most simple and readable way of doing so:
let options = {name:"James"}
const default_options = {name:"John", surname:"Doe"}
options = Object.assign({}, default_options, options)
Object.assign() reference

This sure sounds like the typical use of protoype-based objects:
// define a new type of object
var foo = function() {};
// define a default attribute and value that all objects of this type will have
foo.prototype.attribute1 = "defaultValue1";
// create a new object of my type
var emptyObj = new foo();
console.log(emptyObj.attribute1); // outputs defaultValue1

I think the simplest approach is using Object.assign.
If you have this Class:
class MyHelper {
constructor(options) {
this.options = Object.assign({
name: "John",
surname: "Doe",
birthDate: "1980-08-08"
}, options);
}
}
You can use it like this:
let helper = new MyHelper({ name: "Mark" });
console.log(helper.options.surname); // this will output "Doe"
Documentation (with polyfill):
https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

Or you can try this
dict = {
'somekey': 'somevalue'
};
val = dict['anotherkey'] || 'anotherval';

Simplest of all Solutions:
dict = {'first': 1,
'second': 2,
'third': 3}
Now,
dict['last'] || 'Excluded'
will return 'Excluded', which is the default value.

If you only have an object that is a single level deep (nested object properties will not merge as expected since it directly destructures from the first level), you can use the following destructuring syntax:
const options = {
somevar: 1234,
admin: true
};
const defaults = {
test: false,
admin: false,
};
var mergedOptions = {...defaults, ...options};
Of which the output would be:
console.log(options);
// { somevar: 1234, admin: true }
console.log(mergedOptions);
// { test: false, admin: true, somevar: 1234 }
Or even formatted as a single statement (this is slightly unreadable though):
const options = {...{
// Defaults
test: false,
admin: false,
}, ...{
// Overrides
somevar: 1234,
admin: true
}};

I saw an article yesterday that mentions an Object.__noSuchMethod__ property: JavascriptTips I've not had a chance to play around with it, so I don't know about browser support, but maybe you could use that in some way?

I'm surprised nobody has mentioned ternary operator yet.
var emptyObj = {a:'123', b:'234', c:0};
var defaultValue = 'defaultValue';
var attr = 'someNonExistAttribute';
emptyObj.hasOwnProperty(attr) ? emptyObj[attr] : defaultValue;//=> 'defaultValue'
attr = 'c'; // => 'c'
emptyObj.hasOwnProperty(attr) ? emptyObj[attr] : defaultValue; // => 0
In this way, even if the value of 'c' is 0, it will still get the correct value.

var obj = {
a: 2,
b: 4
};
console.log(obj);
--> {a: 2, b: 4}
function applyDefaults(obj) {
obj.a ||= 10;
obj.b ||= 10;
obj.c ||= 10;
}
// do some magic
applyDefaults(obj);
console.log(obj);
--> {a: 2, b: 4, c: 10}
This works because
undefined || "1111111" --> "1111111"
"0000000" || "1111111" --> "0000000"
as null, undefined, NaN, 0, "" (Empty String), false itself, are all considered to be equivalent to false (falsy). Anything else is true (truthy).
Note that this is not uniformly supported across browsers and nodejs versions (confirm for yourself).
So two troublesome cases are the empty String "" and 0 (zero). If it is important not to override those, you might need to rewrite this as:
if (typeof obj.d == "undefined") obj.d = "default"
This will be better supported across browsers also.
Alternatively you could write this as:
obj.d ??= "default"
This is the nullish assignment which applies only to values that are null or undefined (nullish) - of which the empty string is not part. However, this has again a diminished cross-browser support.
See also on the official Mozilla Website - Assigning a default value to a variable.

This is actually possible to do with Object.create. It will not work for "non defined" properties. But for the ones that has been given a default value.
var defaults = {
a: 'test1',
b: 'test2'
};
Then when you create your properties object you do it with Object.create
properties = Object.create(defaults);
Now you will have two object where the first object is empty, but the prototype points to the defaults object. To test:
console.log('Unchanged', properties);
properties.a = 'updated';
console.log('Updated', properties);
console.log('Defaults', Object.getPrototypeOf(properties));

Object.withDefault = (defaultValue,o={}) => {
return new Proxy(o, {
get: (o, k) => (k in o) ? o[k] : defaultValue
});
}
o = Object.withDefault(42);
o.x //=> 42
o.x = 10
o.x //=> 10
o.xx //=> 42

One approach would be to take a defaults object and merge it with the target object. The target object would override values in the defaults object.
jQuery has the .extend() method that does this. jQuery is not needed however as there are vanilla JS implementations such as can be found here:
http://gomakethings.com/vanilla-javascript-version-of-jquery-extend/

With the addition of the Logical nullish assignment operator, you can now do something like this
const obj = {}
obj.a ??= "default";
In the case where you have an empty list as the default value and want to push to it, you could do
const obj = {}
(obj.a ??= []).push("some value")

I came here looking for a solution because the header matched my problem description but it isn't what i was looking for but i got a solution to my problem(I wanted to have a default value for an attribute which would be dynamic something like date).
let Blog = {
title : String,
image : String,
body : String,
created: {type: Date, default: Date.now}
}
The above code was the solution for which i finally settled.

Related

I want to access the array of object as defined below [duplicate]

I have JavaScript object array with the following structure:
objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
I want to extract a field from each object, and get an array containing the values, for example field foo would give array [ 1, 3, 5 ].
I can do this with this trivial approach:
function getFields(input, field) {
var output = [];
for (var i=0; i < input.length ; ++i)
output.push(input[i][field]);
return output;
}
var result = getFields(objArray, "foo"); // returns [ 1, 3, 5 ]
Is there a more elegant or idiomatic way to do this, so that a custom utility function would be unnecessary?
Note about suggested duplicate, it covers how to convert a single object to an array.
Here is a shorter way of achieving it:
let result = objArray.map(a => a.foo);
OR
let result = objArray.map(({ foo }) => foo)
You can also check Array.prototype.map().
Yes, but it relies on an ES5 feature of JavaScript. This means it will not work in IE8 or older.
var result = objArray.map(function(a) {return a.foo;});
On ES6 compatible JS interpreters you can use an arrow function for brevity:
var result = objArray.map(a => a.foo);
Array.prototype.map documentation
Speaking for the JS only solutions, I've found that, inelegant as it may be, a simple indexed for loop is more performant than its alternatives.
Extracting single property from a 100000 element array (via jsPerf)
Traditional for loop 368 Ops/sec
var vals=[];
for(var i=0;i<testArray.length;i++){
vals.push(testArray[i].val);
}
ES6 for..of loop 303 Ops/sec
var vals=[];
for(var item of testArray){
vals.push(item.val);
}
Array.prototype.map 19 Ops/sec
var vals = testArray.map(function(a) {return a.val;});
TL;DR - .map() is slow, but feel free to use it if you feel readability is worth more than performance.
Edit #2: 6/2019 - jsPerf link broken, removed.
Check out Lodash's _.pluck() function or Underscore's _.pluck() function. Both do exactly what you want in a single function call!
var result = _.pluck(objArray, 'foo');
Update: _.pluck() has been removed as of Lodash v4.0.0, in favour of _.map() in combination with something similar to Niet's answer. _.pluck() is still available in Underscore.
Update 2: As Mark points out in the comments, somewhere between Lodash v4 and 4.3, a new function has been added that provides this functionality again. _.property() is a shorthand function that returns a function for getting the value of a property in an object.
Additionally, _.map() now allows a string to be passed in as the second parameter, which is passed into _.property(). As a result, the following two lines are equivalent to the code sample above from pre-Lodash 4.
var result = _.map(objArray, 'foo');
var result = _.map(objArray, _.property('foo'));
_.property(), and hence _.map(), also allow you to provide a dot-separated string or array in order to access sub-properties:
var objArray = [
{
someProperty: { aNumber: 5 }
},
{
someProperty: { aNumber: 2 }
},
{
someProperty: { aNumber: 9 }
}
];
var result = _.map(objArray, _.property('someProperty.aNumber'));
var result = _.map(objArray, _.property(['someProperty', 'aNumber']));
Both _.map() calls in the above example will return [5, 2, 9].
If you're a little more into functional programming, take a look at Ramda's R.pluck() function, which would look something like this:
var result = R.pluck('foo')(objArray); // or just R.pluck('foo', objArray)
Example to collect the different fields from the object array
let inputArray = [
{ id: 1, name: "name1", value: "value1" },
{ id: 2, name: "name2", value: "value2" },
];
let ids = inputArray.map( (item) => item.id);
let names = inputArray.map((item) => item.name);
let values = inputArray.map((item) => item.value);
console.log(ids);
console.log(names);
console.log(values);
Result :
[ 1, 2 ]
[ 'name1', 'name2' ]
[ 'value1', 'value2' ]
It is better to use some sort of libraries like lodash or underscore for cross browser assurance.
In Lodash you can get values of a property in array by following method
_.map(objArray,"foo")
and in Underscore
_.pluck(objArray,"foo")
Both will return
[1, 2, 3]
Using Array.prototype.map:
function getFields(input, field) {
return input.map(function(o) {
return o[field];
});
}
See the above link for a shim for pre-ES5 browsers.
In ES6, you can do:
const objArray = [{foo: 1, bar: 2}, {foo: 3, bar: 4}, {foo: 5, bar: 6}]
objArray.map(({ foo }) => foo)
If you want multiple values in ES6+ the following will work
objArray = [ { foo: 1, bar: 2, baz: 9}, { foo: 3, bar: 4, baz: 10}, { foo: 5, bar: 6, baz: 20} ];
let result = objArray.map(({ foo, baz }) => ({ foo, baz }))
This works as {foo, baz} on the left is using object destructoring and on the right side of the arrow is equivalent to {foo: foo, baz: baz} due to ES6's enhanced object literals.
While map is a proper solution to select 'columns' from a list of objects, it has a downside. If not explicitly checked whether or not the columns exists, it'll throw an error and (at best) provide you with undefined.
I'd opt for a reduce solution, which can simply ignore the property or even set you up with a default value.
function getFields(list, field) {
// reduce the provided list to an array only containing the requested field
return list.reduce(function(carry, item) {
// check if the item is actually an object and does contain the field
if (typeof item === 'object' && field in item) {
carry.push(item[field]);
}
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
jsbin example
This would work even if one of the items in the provided list is not an object or does not contain the field.
It can even be made more flexible by negotiating a default value should an item not be an object or not contain the field.
function getFields(list, field, otherwise) {
// reduce the provided list to an array containing either the requested field or the alternative value
return list.reduce(function(carry, item) {
// If item is an object and contains the field, add its value and the value of otherwise if not
carry.push(typeof item === 'object' && field in item ? item[field] : otherwise);
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
jsbin example
This would be the same with map, as the length of the returned array would be the same as the provided array. (In which case a map is slightly cheaper than a reduce):
function getFields(list, field, otherwise) {
// map the provided list to an array containing either the requested field or the alternative value
return list.map(function(item) {
// If item is an object and contains the field, add its value and the value of otherwise if not
return typeof item === 'object' && field in item ? item[field] : otherwise;
}, []);
}
jsbin example
And then there is the most flexible solution, one which lets you switch between both behaviours simply by providing an alternative value.
function getFields(list, field, otherwise) {
// determine once whether or not to use the 'otherwise'
var alt = typeof otherwise !== 'undefined';
// reduce the provided list to an array only containing the requested field
return list.reduce(function(carry, item) {
// If item is an object and contains the field, add its value and the value of 'otherwise' if it was provided
if (typeof item === 'object' && field in item) {
carry.push(item[field]);
}
else if (alt) {
carry.push(otherwise);
}
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
jsbin example
As the examples above (hopefully) shed some light on the way this works, lets shorten the function a bit by utilising the Array.concat function.
function getFields(list, field, otherwise) {
var alt = typeof otherwise !== 'undefined';
return list.reduce(function(carry, item) {
return carry.concat(typeof item === 'object' && field in item ? item[field] : (alt ? otherwise : []));
}, []);
}
jsbin example
The above answer is good for a single property but when select multiple properties from an array use this
var arrayObj=[{Name,'A',Age:20,Email:'a.gmail.com'},{Name,'B',Age:30,Email:'b.gmail.com'},{Name,'C',Age:40,Email:'c.gmail.com'}]
now I select only two fields
var outPutArray=arrayObj.map(( {Name,Email} ) => ({Name,Email}) )
console.log(outPutArray)
If you want to also support array-like objects, use Array.from (ES2015):
Array.from(arrayLike, x => x.foo);
The advantage it has over Array.prototype.map() method is the input can also be a Set:
let arrayLike = new Set([{foo: 1}, {foo: 2}, {foo: 3}]);
In general, if you want to extrapolate object values which are inside an array (like described in the question) then you could use reduce, map and array destructuring.
ES6
let a = [{ z: 'word', c: 'again', d: 'some' }, { u: '1', r: '2', i: '3' }];
let b = a.reduce((acc, obj) => [...acc, Object.values(obj).map(y => y)], []);
console.log(b)
The equivalent using for in loop would be:
for (let i in a) {
let temp = [];
for (let j in a[i]) {
temp.push(a[i][j]);
}
array.push(temp);
}
Produced output: ["word", "again", "some", "1", "2", "3"]
If you have nested arrays you can make it work like this:
const objArray = [
{ id: 1, items: { foo:4, bar: 2}},
{ id: 2, items: { foo:3, bar: 2}},
{ id: 3, items: { foo:1, bar: 2}}
];
let result = objArray.map(({id, items: {foo}}) => ({id, foo}))
console.log(result)
Easily extracting multiple properties from array of objects:
let arrayOfObjects = [
{id:1, name:'one', desc:'something'},
{id:2, name:'two', desc:'something else'}
];
//below will extract just the id and name
let result = arrayOfObjects.map(({id, name}) => ({id, name}));
result will be [{id:1, name:'one'},{id:2, name:'two'}]
Add or remove properties as needed in the map function
In ES6, in case you want to dynamically pass the field as a string:
function getFields(array, field) {
return array.map(a => a[field]);
}
let result = getFields(array, 'foo');
It depends on your definition of "better".
The other answers point out the use of map, which is natural (especially for guys used to functional style) and concise. I strongly recommend using it (if you don't bother with the few IE8- IT guys). So if "better" means "more concise", "maintainable", "understandable" then yes, it's way better.
On the other hand, this beauty doesn't come without additional costs. I'm not a big fan of microbench, but I've put up a small test here. The results are predictable, the old ugly way seems to be faster than the map function. So if "better" means "faster", then no, stay with the old school fashion.
Again this is just a microbench and in no way advocating against the use of map, it's just my two cents :).
create an empty array then forEach element from your list, push what you want from that object into your empty array.
let objArray2 = [];
objArray.forEach(arr => objArray2.push(arr.foo));
From an array of objects, extract the value of a property as an array with for loop.
//input
objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
//Code
let output=[];
for(let item of objArray){
output.push(item.foo);
}
// Output
[ 1, 3, 5 ]
Above provided answer is good for extracting single property, what if you want to extract more than one property from array of objects.
Here is the solution!!
In case of that we can simply use _.pick(object, [paths])
_.pick(object, [paths])
Lets assume objArray has objects with three properties like below
objArray = [ { foo: 1, bar: 2, car:10}, { foo: 3, bar: 4, car:10}, { foo: 5, bar: 6, car:10} ];
Now we want to extract foo and bar property from every object and store them in a separate array.
First we will iterate array elements using map and then we apply Lodash Library Standard _.pick() method on it.
Now we are able to extract 'foo' and 'bar' property.
var newArray = objArray.map((element)=>{ return _.pick(element, ['foo','bar'])})
console.log(newArray);
and result would be
[{foo: 1, bar: 2},{foo: 3, bar: 4},{foo: 5, bar: 6}]
enjoy!!!
Here is another shape of using map method on array of objects to get back specific property:
const objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
const getProp = prop => obj => obj[prop];
const getFoo = getProp('foo');
const fooes = objArray.map(getFoo);
console.log(fooes);
I would only improve one of the answers if you even don't know the exact property of the object you'r playing with use below:
let result = objArray.map(a => a[Object.getOwnPropertyNames(a)]);
Function map is a good choice when dealing with object arrays. Although there have been a number of good answers posted already, the example of using map with combination with filter might be helpful.
In case you want to exclude the properties which values are undefined or exclude just a specific property, you could do the following:
var obj = {value1: "val1", value2: "val2", Ndb_No: "testing", myVal: undefined};
var keysFiltered = Object.keys(obj).filter(function(item){return !(item == "Ndb_No" || obj[item] == undefined)});
var valuesFiltered = keysFiltered.map(function(item) {return obj[item]});
https://jsfiddle.net/ohea7mgk/
Destructure and get specific attributes from array of object:
const customerList = dealerUserData?.partyDetails.map(
({ partyId, custAccountId }) => ({
partyId,
custAccountId,
customerId: dealerUserData?._id,
userId: dealerUserData?.authUserID,
}),
);

Intersecting objects with a default object in JavaScript

I have an object with objects, which are basically settings per guild. In these objects are various configuration options that the admin of their guild can change.
{
"1": {
"foo": "Hello World",
"bar": "Ello World",
"roo": {
"doo": "oof"
}
},
"2": {
"foo": "foo bar foo bar",
"bar": "World! Hi!",
"roo": {
"doo": "boo!"
}
}
}
And I have a default object for those settings.
const Default = {
foo: "Hello, World!",
bar: "Foo example",
roo: {
doo: "boo"
}
};
When I add a new key to the default object, I'd like all the objects in the settings to adapt to these new changes and add only the new key with the default value. Although, I don't know which key is new, because only at startup I want the script to check for new values and add those to the existing settings.
Also, settings can have nested objects but no arrays - arrays are only used as value. I've looked at this answer but I cannot seem to figure out how to add nested object support
For instance, I could add a "foobarexample" key to the default object, and as default value "ello" and it would add that to all the settings. I should also be able to add this to the 'roo' object and it would still update, even though 'roo' isn't new
If you have any suggestions, they're much appreciated! Thanks
This is a perfect use case for JavaScript's prototypal inheritance:
const defaultObj = {
foo: "Hello, World!",
bar: "Foo example",
roo: {
doo: "boo"
}
};
const obj = Object.assign(Object.create(defaultObj), { foo: "New foo" });
const anotherObj = Object.create(defaultObj);
console.log('obj.foo', obj.foo);
console.log('anotherObj.foo', anotherObj.foo);
console.log('obj.bar', obj.bar);
defaultObj.bar = "New bar";
console.log('obj.bar', obj.bar);
console.log('anotherObj.bar', anotherObj.bar);
defaultObj.foobarexample = "ello";
console.log('obj.foobarexample ', obj.foobarexample );
console.log('anotherObj.foobarexample ', anotherObj.foobarexample );
In this code, all of the objects created from defaultObj with Object.create have a reference on defaultObj. When trying to access a property of the resulting objects, the property will be looked up in the object itself, and then in defaultObj if not found. defaultObj can safely be mutated after the objects have been created (it cannot be reassigned though, because the objects created would keep a reference on the previous one).
https://repl.it/#HenriDe/httpsreplitHenriDeIntersectinobjectswithdefault
this one should work for what you are looking for.
const defaultValues = {
"1" : {
foo: 'Hello, World!',
bar: 'Foo example',
roo: {
doo: 'boo',
newest: {
goal: "dodo"
}
},
hello: "world"
}
};
function eachRecursive(obj, defaultValues, level = 0){
// check each key amongst the defaults
for (var k in defaultValues){
// if our original object does not have key attach it + default values associated
if (!obj.hasOwnProperty(k)) {
obj[k] = checkWith[k]
}
// keep calling as long as value is object
if (typeof obj[k] == "object" && obj[k] !== null)
eachRecursive(obj[k], checkWith[k], level + 1);
}
}
}
// for each of the specific entries check
for (let [key, value] of Object.entries(data)) {
eachRecursive(data[key], defaultValues["1"])
}
console.log(data["1"])

Is there a way to capture a variable access? [duplicate]

Is there a way to set the default attribute of a Javascript object such that:
let emptyObj = {};
// do some magic
emptyObj.nonExistingAttribute // => defaultValue
Since I asked the question several years ago things have progressed nicely.
Proxies are part of ES6. The following example works in Chrome, Firefox, Safari and Edge:
let handler = {
get: function(target, name) {
return target.hasOwnProperty(name) ? target[name] : 42;
}
};
let emptyObj = {};
let p = new Proxy(emptyObj, handler);
p.answerToTheUltimateQuestionOfLife; //=> 42
Read more in Mozilla's documentation on Proxies.
Use destructuring (new in ES6)
There is great documentation by Mozila as well as a fantastic blog post that explains the syntax better than I can.
To Answer Your Question
var emptyObj = {};
const { nonExistingAttribute = defaultValue } = emptyObj;
console.log(nonExistingAttribute); // defaultValue
Going Further
Can I rename this variable? Sure!
const { nonExistingAttribute: coolerName = 15} = emptyObj;
console.log(coolerName); // 15
What about nested data? Bring it on!
var nestedData = {
name: 'Awesome Programmer',
languages: [
{
name: 'javascript',
proficiency: 4,
}
],
country: 'Canada',
};
var {name: realName, languages: [{name: languageName}]} = nestedData ;
console.log(realName); // Awesome Programmer
console.log(languageName); // javascript
There isn't a way to set this in Javascript - returning undefined for non-existent properties is a part of the core Javascript spec. See the discussion for this similar question. As I suggested there, one approach (though I can't really recommend it) would be to define a global getProperty function:
function getProperty(o, prop) {
if (o[prop] !== undefined) return o[prop];
else return "my default";
}
var o = {
foo: 1
};
getProperty(o, 'foo'); // 1
getProperty(o, 'bar'); // "my default"
But this would lead to a bunch of non-standard code that would be difficult for others to read, and it might have unintended consequences in areas where you'd expect or want an undefined value. Better to just check as you go:
var someVar = o.someVar || "my default";
my code is:
function(s){
s = {
top: s.top || 100, // default value or s.top
left: s.left || 300, // default value or s.left
}
alert(s.top)
}
The way I achieve this is with the object.assign function
const defaultProperties = { 'foo': 'bar', 'bar': 'foo' };
const overwriteProperties = { 'foo': 'foo' };
const newObj = Object.assign({}, defaultProperties, overwriteProperties);
console.log(defaultProperties); // {"foo": "bar", "bar": "foo"}
console.log(overwriteProperties); // { "foo": "foo" };
console.log(newObj); // { "foo": "foo", "bar": "foo" }
This seems to me the most simple and readable way of doing so:
let options = {name:"James"}
const default_options = {name:"John", surname:"Doe"}
options = Object.assign({}, default_options, options)
Object.assign() reference
This sure sounds like the typical use of protoype-based objects:
// define a new type of object
var foo = function() {};
// define a default attribute and value that all objects of this type will have
foo.prototype.attribute1 = "defaultValue1";
// create a new object of my type
var emptyObj = new foo();
console.log(emptyObj.attribute1); // outputs defaultValue1
I think the simplest approach is using Object.assign.
If you have this Class:
class MyHelper {
constructor(options) {
this.options = Object.assign({
name: "John",
surname: "Doe",
birthDate: "1980-08-08"
}, options);
}
}
You can use it like this:
let helper = new MyHelper({ name: "Mark" });
console.log(helper.options.surname); // this will output "Doe"
Documentation (with polyfill):
https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Or you can try this
dict = {
'somekey': 'somevalue'
};
val = dict['anotherkey'] || 'anotherval';
Simplest of all Solutions:
dict = {'first': 1,
'second': 2,
'third': 3}
Now,
dict['last'] || 'Excluded'
will return 'Excluded', which is the default value.
If you only have an object that is a single level deep (nested object properties will not merge as expected since it directly destructures from the first level), you can use the following destructuring syntax:
const options = {
somevar: 1234,
admin: true
};
const defaults = {
test: false,
admin: false,
};
var mergedOptions = {...defaults, ...options};
Of which the output would be:
console.log(options);
// { somevar: 1234, admin: true }
console.log(mergedOptions);
// { test: false, admin: true, somevar: 1234 }
Or even formatted as a single statement (this is slightly unreadable though):
const options = {...{
// Defaults
test: false,
admin: false,
}, ...{
// Overrides
somevar: 1234,
admin: true
}};
I saw an article yesterday that mentions an Object.__noSuchMethod__ property: JavascriptTips I've not had a chance to play around with it, so I don't know about browser support, but maybe you could use that in some way?
I'm surprised nobody has mentioned ternary operator yet.
var emptyObj = {a:'123', b:'234', c:0};
var defaultValue = 'defaultValue';
var attr = 'someNonExistAttribute';
emptyObj.hasOwnProperty(attr) ? emptyObj[attr] : defaultValue;//=> 'defaultValue'
attr = 'c'; // => 'c'
emptyObj.hasOwnProperty(attr) ? emptyObj[attr] : defaultValue; // => 0
In this way, even if the value of 'c' is 0, it will still get the correct value.
var obj = {
a: 2,
b: 4
};
console.log(obj);
--> {a: 2, b: 4}
function applyDefaults(obj) {
obj.a ||= 10;
obj.b ||= 10;
obj.c ||= 10;
}
// do some magic
applyDefaults(obj);
console.log(obj);
--> {a: 2, b: 4, c: 10}
This works because
undefined || "1111111" --> "1111111"
"0000000" || "1111111" --> "0000000"
as null, undefined, NaN, 0, "" (Empty String), false itself, are all considered to be equivalent to false (falsy). Anything else is true (truthy).
Note that this is not uniformly supported across browsers and nodejs versions (confirm for yourself).
So two troublesome cases are the empty String "" and 0 (zero). If it is important not to override those, you might need to rewrite this as:
if (typeof obj.d == "undefined") obj.d = "default"
This will be better supported across browsers also.
Alternatively you could write this as:
obj.d ??= "default"
This is the nullish assignment which applies only to values that are null or undefined (nullish) - of which the empty string is not part. However, this has again a diminished cross-browser support.
See also on the official Mozilla Website - Assigning a default value to a variable.
This is actually possible to do with Object.create. It will not work for "non defined" properties. But for the ones that has been given a default value.
var defaults = {
a: 'test1',
b: 'test2'
};
Then when you create your properties object you do it with Object.create
properties = Object.create(defaults);
Now you will have two object where the first object is empty, but the prototype points to the defaults object. To test:
console.log('Unchanged', properties);
properties.a = 'updated';
console.log('Updated', properties);
console.log('Defaults', Object.getPrototypeOf(properties));
Object.withDefault = (defaultValue,o={}) => {
return new Proxy(o, {
get: (o, k) => (k in o) ? o[k] : defaultValue
});
}
o = Object.withDefault(42);
o.x //=> 42
o.x = 10
o.x //=> 10
o.xx //=> 42
One approach would be to take a defaults object and merge it with the target object. The target object would override values in the defaults object.
jQuery has the .extend() method that does this. jQuery is not needed however as there are vanilla JS implementations such as can be found here:
http://gomakethings.com/vanilla-javascript-version-of-jquery-extend/
With the addition of the Logical nullish assignment operator, you can now do something like this
const obj = {}
obj.a ??= "default";
In the case where you have an empty list as the default value and want to push to it, you could do
const obj = {}
(obj.a ??= []).push("some value")
I came here looking for a solution because the header matched my problem description but it isn't what i was looking for but i got a solution to my problem(I wanted to have a default value for an attribute which would be dynamic something like date).
let Blog = {
title : String,
image : String,
body : String,
created: {type: Date, default: Date.now}
}
The above code was the solution for which i finally settled.

What is the correct syntax for connecting a variable name to one of its array properties in JavaScript? [duplicate]

I have JavaScript object array with the following structure:
objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
I want to extract a field from each object, and get an array containing the values, for example field foo would give array [ 1, 3, 5 ].
I can do this with this trivial approach:
function getFields(input, field) {
var output = [];
for (var i=0; i < input.length ; ++i)
output.push(input[i][field]);
return output;
}
var result = getFields(objArray, "foo"); // returns [ 1, 3, 5 ]
Is there a more elegant or idiomatic way to do this, so that a custom utility function would be unnecessary?
Note about suggested duplicate, it covers how to convert a single object to an array.
Here is a shorter way of achieving it:
let result = objArray.map(a => a.foo);
OR
let result = objArray.map(({ foo }) => foo)
You can also check Array.prototype.map().
Yes, but it relies on an ES5 feature of JavaScript. This means it will not work in IE8 or older.
var result = objArray.map(function(a) {return a.foo;});
On ES6 compatible JS interpreters you can use an arrow function for brevity:
var result = objArray.map(a => a.foo);
Array.prototype.map documentation
Speaking for the JS only solutions, I've found that, inelegant as it may be, a simple indexed for loop is more performant than its alternatives.
Extracting single property from a 100000 element array (via jsPerf)
Traditional for loop 368 Ops/sec
var vals=[];
for(var i=0;i<testArray.length;i++){
vals.push(testArray[i].val);
}
ES6 for..of loop 303 Ops/sec
var vals=[];
for(var item of testArray){
vals.push(item.val);
}
Array.prototype.map 19 Ops/sec
var vals = testArray.map(function(a) {return a.val;});
TL;DR - .map() is slow, but feel free to use it if you feel readability is worth more than performance.
Edit #2: 6/2019 - jsPerf link broken, removed.
Check out Lodash's _.pluck() function or Underscore's _.pluck() function. Both do exactly what you want in a single function call!
var result = _.pluck(objArray, 'foo');
Update: _.pluck() has been removed as of Lodash v4.0.0, in favour of _.map() in combination with something similar to Niet's answer. _.pluck() is still available in Underscore.
Update 2: As Mark points out in the comments, somewhere between Lodash v4 and 4.3, a new function has been added that provides this functionality again. _.property() is a shorthand function that returns a function for getting the value of a property in an object.
Additionally, _.map() now allows a string to be passed in as the second parameter, which is passed into _.property(). As a result, the following two lines are equivalent to the code sample above from pre-Lodash 4.
var result = _.map(objArray, 'foo');
var result = _.map(objArray, _.property('foo'));
_.property(), and hence _.map(), also allow you to provide a dot-separated string or array in order to access sub-properties:
var objArray = [
{
someProperty: { aNumber: 5 }
},
{
someProperty: { aNumber: 2 }
},
{
someProperty: { aNumber: 9 }
}
];
var result = _.map(objArray, _.property('someProperty.aNumber'));
var result = _.map(objArray, _.property(['someProperty', 'aNumber']));
Both _.map() calls in the above example will return [5, 2, 9].
If you're a little more into functional programming, take a look at Ramda's R.pluck() function, which would look something like this:
var result = R.pluck('foo')(objArray); // or just R.pluck('foo', objArray)
Example to collect the different fields from the object array
let inputArray = [
{ id: 1, name: "name1", value: "value1" },
{ id: 2, name: "name2", value: "value2" },
];
let ids = inputArray.map( (item) => item.id);
let names = inputArray.map((item) => item.name);
let values = inputArray.map((item) => item.value);
console.log(ids);
console.log(names);
console.log(values);
Result :
[ 1, 2 ]
[ 'name1', 'name2' ]
[ 'value1', 'value2' ]
It is better to use some sort of libraries like lodash or underscore for cross browser assurance.
In Lodash you can get values of a property in array by following method
_.map(objArray,"foo")
and in Underscore
_.pluck(objArray,"foo")
Both will return
[1, 2, 3]
Using Array.prototype.map:
function getFields(input, field) {
return input.map(function(o) {
return o[field];
});
}
See the above link for a shim for pre-ES5 browsers.
In ES6, you can do:
const objArray = [{foo: 1, bar: 2}, {foo: 3, bar: 4}, {foo: 5, bar: 6}]
objArray.map(({ foo }) => foo)
If you want multiple values in ES6+ the following will work
objArray = [ { foo: 1, bar: 2, baz: 9}, { foo: 3, bar: 4, baz: 10}, { foo: 5, bar: 6, baz: 20} ];
let result = objArray.map(({ foo, baz }) => ({ foo, baz }))
This works as {foo, baz} on the left is using object destructoring and on the right side of the arrow is equivalent to {foo: foo, baz: baz} due to ES6's enhanced object literals.
While map is a proper solution to select 'columns' from a list of objects, it has a downside. If not explicitly checked whether or not the columns exists, it'll throw an error and (at best) provide you with undefined.
I'd opt for a reduce solution, which can simply ignore the property or even set you up with a default value.
function getFields(list, field) {
// reduce the provided list to an array only containing the requested field
return list.reduce(function(carry, item) {
// check if the item is actually an object and does contain the field
if (typeof item === 'object' && field in item) {
carry.push(item[field]);
}
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
jsbin example
This would work even if one of the items in the provided list is not an object or does not contain the field.
It can even be made more flexible by negotiating a default value should an item not be an object or not contain the field.
function getFields(list, field, otherwise) {
// reduce the provided list to an array containing either the requested field or the alternative value
return list.reduce(function(carry, item) {
// If item is an object and contains the field, add its value and the value of otherwise if not
carry.push(typeof item === 'object' && field in item ? item[field] : otherwise);
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
jsbin example
This would be the same with map, as the length of the returned array would be the same as the provided array. (In which case a map is slightly cheaper than a reduce):
function getFields(list, field, otherwise) {
// map the provided list to an array containing either the requested field or the alternative value
return list.map(function(item) {
// If item is an object and contains the field, add its value and the value of otherwise if not
return typeof item === 'object' && field in item ? item[field] : otherwise;
}, []);
}
jsbin example
And then there is the most flexible solution, one which lets you switch between both behaviours simply by providing an alternative value.
function getFields(list, field, otherwise) {
// determine once whether or not to use the 'otherwise'
var alt = typeof otherwise !== 'undefined';
// reduce the provided list to an array only containing the requested field
return list.reduce(function(carry, item) {
// If item is an object and contains the field, add its value and the value of 'otherwise' if it was provided
if (typeof item === 'object' && field in item) {
carry.push(item[field]);
}
else if (alt) {
carry.push(otherwise);
}
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
jsbin example
As the examples above (hopefully) shed some light on the way this works, lets shorten the function a bit by utilising the Array.concat function.
function getFields(list, field, otherwise) {
var alt = typeof otherwise !== 'undefined';
return list.reduce(function(carry, item) {
return carry.concat(typeof item === 'object' && field in item ? item[field] : (alt ? otherwise : []));
}, []);
}
jsbin example
The above answer is good for a single property but when select multiple properties from an array use this
var arrayObj=[{Name,'A',Age:20,Email:'a.gmail.com'},{Name,'B',Age:30,Email:'b.gmail.com'},{Name,'C',Age:40,Email:'c.gmail.com'}]
now I select only two fields
var outPutArray=arrayObj.map(( {Name,Email} ) => ({Name,Email}) )
console.log(outPutArray)
If you want to also support array-like objects, use Array.from (ES2015):
Array.from(arrayLike, x => x.foo);
The advantage it has over Array.prototype.map() method is the input can also be a Set:
let arrayLike = new Set([{foo: 1}, {foo: 2}, {foo: 3}]);
In general, if you want to extrapolate object values which are inside an array (like described in the question) then you could use reduce, map and array destructuring.
ES6
let a = [{ z: 'word', c: 'again', d: 'some' }, { u: '1', r: '2', i: '3' }];
let b = a.reduce((acc, obj) => [...acc, Object.values(obj).map(y => y)], []);
console.log(b)
The equivalent using for in loop would be:
for (let i in a) {
let temp = [];
for (let j in a[i]) {
temp.push(a[i][j]);
}
array.push(temp);
}
Produced output: ["word", "again", "some", "1", "2", "3"]
If you have nested arrays you can make it work like this:
const objArray = [
{ id: 1, items: { foo:4, bar: 2}},
{ id: 2, items: { foo:3, bar: 2}},
{ id: 3, items: { foo:1, bar: 2}}
];
let result = objArray.map(({id, items: {foo}}) => ({id, foo}))
console.log(result)
Easily extracting multiple properties from array of objects:
let arrayOfObjects = [
{id:1, name:'one', desc:'something'},
{id:2, name:'two', desc:'something else'}
];
//below will extract just the id and name
let result = arrayOfObjects.map(({id, name}) => ({id, name}));
result will be [{id:1, name:'one'},{id:2, name:'two'}]
Add or remove properties as needed in the map function
In ES6, in case you want to dynamically pass the field as a string:
function getFields(array, field) {
return array.map(a => a[field]);
}
let result = getFields(array, 'foo');
It depends on your definition of "better".
The other answers point out the use of map, which is natural (especially for guys used to functional style) and concise. I strongly recommend using it (if you don't bother with the few IE8- IT guys). So if "better" means "more concise", "maintainable", "understandable" then yes, it's way better.
On the other hand, this beauty doesn't come without additional costs. I'm not a big fan of microbench, but I've put up a small test here. The results are predictable, the old ugly way seems to be faster than the map function. So if "better" means "faster", then no, stay with the old school fashion.
Again this is just a microbench and in no way advocating against the use of map, it's just my two cents :).
create an empty array then forEach element from your list, push what you want from that object into your empty array.
let objArray2 = [];
objArray.forEach(arr => objArray2.push(arr.foo));
From an array of objects, extract the value of a property as an array with for loop.
//input
objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
//Code
let output=[];
for(let item of objArray){
output.push(item.foo);
}
// Output
[ 1, 3, 5 ]
Above provided answer is good for extracting single property, what if you want to extract more than one property from array of objects.
Here is the solution!!
In case of that we can simply use _.pick(object, [paths])
_.pick(object, [paths])
Lets assume objArray has objects with three properties like below
objArray = [ { foo: 1, bar: 2, car:10}, { foo: 3, bar: 4, car:10}, { foo: 5, bar: 6, car:10} ];
Now we want to extract foo and bar property from every object and store them in a separate array.
First we will iterate array elements using map and then we apply Lodash Library Standard _.pick() method on it.
Now we are able to extract 'foo' and 'bar' property.
var newArray = objArray.map((element)=>{ return _.pick(element, ['foo','bar'])})
console.log(newArray);
and result would be
[{foo: 1, bar: 2},{foo: 3, bar: 4},{foo: 5, bar: 6}]
enjoy!!!
Here is another shape of using map method on array of objects to get back specific property:
const objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
const getProp = prop => obj => obj[prop];
const getFoo = getProp('foo');
const fooes = objArray.map(getFoo);
console.log(fooes);
I would only improve one of the answers if you even don't know the exact property of the object you'r playing with use below:
let result = objArray.map(a => a[Object.getOwnPropertyNames(a)]);
Function map is a good choice when dealing with object arrays. Although there have been a number of good answers posted already, the example of using map with combination with filter might be helpful.
In case you want to exclude the properties which values are undefined or exclude just a specific property, you could do the following:
var obj = {value1: "val1", value2: "val2", Ndb_No: "testing", myVal: undefined};
var keysFiltered = Object.keys(obj).filter(function(item){return !(item == "Ndb_No" || obj[item] == undefined)});
var valuesFiltered = keysFiltered.map(function(item) {return obj[item]});
https://jsfiddle.net/ohea7mgk/
Destructure and get specific attributes from array of object:
const customerList = dealerUserData?.partyDetails.map(
({ partyId, custAccountId }) => ({
partyId,
custAccountId,
customerId: dealerUserData?._id,
userId: dealerUserData?.authUserID,
}),
);

javascript: add nested attribute in object

Wit this example object:
obj = {
id: '123',
attr: 'something'
}
Now I want to add the attribute link in the attribute data. Sometimes data is already existing, but in this example data doesn't exist.
So if I do
obj.data.link = 'www.website.com';
I get the error TypeError: Cannot set property 'link' of undefined.
Result should be:
obj = {
id: '123',
attr: 'something',
data: {
link: 'www.website.com'
}
}
You need to create the data object first:
obj.data = {};
obj.data.link = 'www.website.com';
or you can do it all at once:
obj.data = {
link: 'www.website.com'
};
if data may or may not already by there, there's a handy shortcut that will use the existing one if it's there or create a new one if not:
obj.data = obj.data || {};
obj.data.link = 'www.website.com';
That uses the JavaScript's curiously-powerful || operator.
You need to initialize the data property. You can do something like this:
var obj = {
id: '123',
attr: 'something'
};
obj.data = {};
obj.data.link = 'www.website.com';
In the case for the property existing you can check before assigning link.
if (!obj.data) {
obj.data = {};
}
And as stated in another answer you can use the or operator which I've heard is 'curiously powerful' =]
obj.data = obj.data || {};
That basically means if this value ( obj.data ) exists use it and if it doesn't use the right operand ( {} ). This works because of short circuit evaluation.
Javascript From 1.8.5 you can use the following method:
Object.defineProperty(obj, "data", {
value: {'link' : 'www.website.com'},
writable: true,
enumerable: true,
configurable: true
});
Good luck :)

Categories

Resources