Setting a default value for object properties - javascript

Is it possible to multiple values for one property in objects? Here's what I mean:
// Note: This will give you an error if you try this. It's just an example.
var person = {
name: "John",
eyeColor: "blue" | "green"
};
How it should works:
So for eyeColor, the default is blue, but if it's set to green, then it should ignore the blue.
The code above is obviously not executable, but is there something similar I can do to achieve that?

You can achieve this with a bit of reflection.
I've created a rough implementation of a function which takes an object, the name of a property and a default value. It creates get and set methods for the property in question.
function addDefaultProperty(object, propName, defaultValue) {
var propValue;
Object.defineProperty(object, propName, {
// this method is called when the property is set
// e.g. object.propName = value
set: function(value) {
propValue = value;
},
// this method is called when the property is accessed
// e.g. console.log(object.propName)
get: function() {
var hasValue = typeof propValue != 'undefined';
return hasValue ? propValue : defaultValue;
}
});
return object;
}
Then you can simply create your object however you want and use that method to create properties with default values.
var person = {
name: "John",
};
addDefaultProperty(person, 'eyeColor', 'blue');
console.log(person.eyeColor); // "blue"
person.eyeColor = "green";
console.log(person.eyeColor); // "green"
person.eyeColor = undefined;
console.log(person.eyeColor); // "blue"

Here's a very small example of what you're trying to accomplish, and in the same style you're trying to accomplish it... If you don't pass an arg for eyeclr, it will default to 'blue' here.
function newPerson(nm, eyeclr) {
return {
name: nm,
eyeColor: eyeclr || "blue"
};
}
> newPerson('john')
Object {name: "john", eyeColor: "blue"}
> newPerson('john', 'green')
Object {name: "john", eyeColor: "green"}

Is it possible to multiple values for one property in objects?
Yes, something like this will do:
var person = {
name: "John",
eyeColor: ["blue","green"]
};
That answers your first question.
NOTE: I think your logic is a bit incoherent IMHO.
For your second requirement, I think you're better off writing a setter function:
var person = {
name: "John",
eyeColor: "blue",
setEyeColor:function(color){ this.eyeColor=color;}
};
alert(person.eyeColor);
person.setEyeColor('purple');
alert(person.eyeColor);

Related

How to dynamically update an object field in JavaScript/TypeScript

I have a method for handling an update on this object. In this method I want to accept any field value that the object has, such as name, age, weight, eyeColor etc. If that field exists within that object, I'd like to be able to update the object dynamically for any field they pass in.
I am currently doing it incorrectly I believe with the spread operator while trying to update one field in the object. There is an error that fieldName does not exist within myObject. Does anyone know how to do this dynamically without need for a long switch statement checking each of the fields against fieldName passed in? In previous attempts tweaking with this I have successfully added a new field to the object named "fieldName" which is also not what I wanted.
If anyone has any ideas, that would be so helpful, thank you!
let myObject = {
name: 'John',
lastName: 'Smith',
age: 26,
weight: 200,
eyeColor: 'blue',
hairColor: 'blonde'
};
const handleUpdate = (fieldName: string, fieldValue: string | number) => {
if (fieldName in myObject) {
myObject = {...myObject, fieldName: fieldValue};
}
}
handleUpdate('name', 'Jack'); // Want this call to update the name of 'John' to 'Jack'
In short, you're looking for:
{...myObject, [fieldName]: fieldValue}
You can make a generalized, typesafe function to do this as follows:
function updateProp<TObj, K extends keyof TObj>(obj: TObj, key: K, value: TObj[K]) {
return {...obj, [key]: value};
}
and call it as follows:
const foo = { a: 1, b: "monkey" };
const updatedFoo = updateProp(foo, "b", "hello world")
Playground Link
You're looking for the Bracket notation property accessor:
myObject[fieldName] = fieldValue
Compared to the approach with the spread operator, this does actually update the object in place. I.e. if the reference in myObject was previously copied elsewhere, that reference will also "see" the updated field.
Whereas, by overriding the value with myObject = {...myObject}, you're creating a new object each time.

How to iterate an object on TypeScript changing a string value for its corresponding number?

I have a type coming from a 3rd party API which has a lot of properties (50+) and they take all values as string. The even number and booleans became strings ("5" and "false" respectively) and I want to fix the scary thing.
So I created a type like this to receive the response from API and to hold after fix
interface Person {
age: string | number,
name: string,
hasChildren: string | boolean,
...
}
And I want to transform this
const responseFromApi: Person = {
age: "20",
name: "John",
numberOfChildren: "true"
...
}
to
const afterTreatment: Person = {
age: 21,
name: "John",
numberOfChildren: true
...
}
This is an example... My object, again, is much bigger than this, with a lot of props in this situation so treat them individually is not the kind of solution I'm looking for.
My intention is to iterate over the object and change to number or boolean what can be changed following type.
You could for-loop the array, check if the element.age is a string and if yes parseInt the age and set it again.
A better solution would maybe be to map though the list, and do the same thing as above, just so it creates a new array, which you could then overwrite/do what you need.
Idea:
const changed = persons.map((p: Person) => {
if (typeof p.age === "string") {
return {
...p,
age:parseInt(p.age)
}
}
return p
});
This should work as long as the variable conventions are consistent with person1, person2, person3 etc and also you need to know the total number of added persons for the forloop to work
interface Person {
age: string | number,
name: string
}
const person1: Person = {
age: 20,
name: "Jonh",
}
const person2: Person = {
age: "21",
name: "Marie",
}
const lengthOfPersons: number = 2;
const newArray = [];
for(let i = 0; i < lengthOfPersons; i++)
{
const n = i + 1;
const row = eval('person'+n);
newArray.push({...row, age: +row.age})
}
console.log(newArray);

Overriding object's default getter operator

Let's say I have the following object in my JS code:
var myData = {
"someWeirdPrefix_name": "John Doe",
"someWeirdPrefix_age": 24,
...
}
Is there a way to override the default getter operator (.)? As in, if I write myData.name, I would like to get "John Doe" (thanks to the override). Basically I want to override the method so that I can take the key given, add the weird prefix to the key and then get the value. I know the easy approach would be to just clean my actual data and remove the prefix, but that's not acceptable in my use case. Is this actually possible to do for every property (not just name and age)?
Usually Proxy is used for that
const myData = {
"someWeirdPrefix_name": "John Doe",
"someWeirdPrefix_age": 24,
}
const newMyData = new Proxy(myData, {
get(target, prop, receiver) {
return target['someWeirdPrefix_' + prop];
}
})
console.log(newMyData.name)
I suggest you create a proxy over the data object, and on the get handler, you can check if there is a key in myData including the dot notation key, if YES, get the value of that key.
const myData = {
"someWeirdPrefix_name": "John Doe",
"someWeirdPrefix_age": 24,
}
const handler = {
get(target, prop, receiver) {
return target[Object.keys(target).find(key => key.includes(prop))]
}
}
const proxy = new Proxy(myData, handler);
console.log(proxy.name)

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 :)

Javascript: setting object properties

I want to be able to do this:
var user1 = {
name: 'John',
gender: 'male'
}
var user2 = {
name: 'James',
gender: 'male',
email: 'james#gmail.com'
}
user1.someSetMethod({email: 'john#gmail.com'});
user2.someSetMethod({name: 'Jenny', gender: 'female'});
Desired Outcome:
var user1 = {
name: 'John',
gender: 'male',
email: 'john#gmail.com'
}
var user2 = {
name: 'Jenny',
gender: 'female',
email: 'james#gmail.com'
}
I want a method that will set attributes according to what is passed into the function. Is the attribute doesn't exist I want it to be created, if it does, I want it to be overwritten.
Does a method like this exist in Javascript?
This is normally called extending your object. Virtually every JavaScript library has its own method to do this. Or you can write your own in a few lines of code.
jQuery extend
underscore.js extend
ExtJS extend
MooTools extend
Prototype extend
Using jQuery's method, you'd do it like so:
var user1 = {
name: 'John',
gender: 'male'
};
$.extend(user1, {
email: 'john#gmail.com'
});
user1.email === 'john#gmail.com'; //true
No, You don't want to do this.
The only way to add a method to all objects is to extend Object.prototype.
Doing so (in ES3 browsers like IE8) has a consequence of adding properties to enumerations.
For example:
Object.prototype.extend = function (o) {
// clone properties of o into this
};
var someObj = {};
for (var key in someObj) {
console.log(key); // "extend"
}
The best you can do is use Object.extend
Object.extend(user2, {
name: "Jenny",
gender: "female"
});
pd has an implementation of extend or you can use:
Object.extend = function (target, source) {
for (var key in source) {
target[key] = source[key];
}
};
If you are willing to use jquery, you can use it's extend method, along with some defaults that will make sure all of the properties you want are set correctly.
Something like this:
function whatever(obj1) {
var defaults = {
name: 'Foo Bar',
email: 'email#example.com',
gender: 'male'
};
return $.extend(defaults, obj1);
}

Categories

Resources