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)
Related
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.
I have an Javascript object called person with various properties such as id, name, phone, etc.
I want to create a new Javascript object called roster that is just the name. Something like this:
let person = { name: "Hilda", "id": 123, "phone": 000-000-0000 };
let roster = { person.name : person.phone };
However, React throws an error having person.name in the key. It doesn't matter if I do person.name or person["name"]. I have to do:
let roster = {};
roster[person.name] = person.phone;
Is there some special syntax to allow person.name to be set as the key directly, or is the work-around required?
Use []
let person = { name: "Hilda", "id": 123, "phone": "000-000-0000" };
let roster = { [person.name] : person.phone };
console.log(roster)
Vugar's answer is correct, this can be done by placing brackets [] around the first object's property name.
This is called a computed property name. From the MDN web docs:
The object initializer syntax also supports computed property names.
That allows you to put an expression in brackets [], that will be
computed and used as the property name. This is reminiscent of the
bracket notation of the property accessor syntax, which you may have
used to read and set properties already.
Now you can use a similar syntax in object literals, too:
// Computed property names
let i = 0;
const a = {
[`foo${++i}`]: i,
[`foo${++i}`]: i,
[`foo${++i}`]: i,
};
console.log(a.foo1); // 1
console.log(a.foo2); // 2
console.log(a.foo3); // 3
const items = ["A", "B", "C"];
const obj = {
[items]: "Hello",
};
console.log(obj); // A,B,C: "Hello"
console.log(obj["A,B,C"]); // "Hello"
const param = 'size';
const config = {
[param]: 12,
[`mobile${param.charAt(0).toUpperCase()}${param.slice(1)}`]: 4,
};
console.log(config); // {size: 12, mobileSize: 4}
MDN Docs Reference
Ho can I avoid to write all fields with = ""?
const defaultPlayer = {
name: "",
surname: "",
age: "",
skill: ""
}
// ...
mapPropsToValues = ({ player }) => player || defaultPlayer
Is there in javascript that I can use to avoid write all the time = ""?
I mean, if I already know that defalut value of every field is "" (empty string) how can I do instead of write every field explicitly?
You can accomplish this using a with block and a Proxy object and eval:
let defaultPlayer;
with (new Proxy({}, {
has(o, key) {
try { eval(key); }
catch (e) { return true; }
},
get() {
return '';
}
})) {
defaultPlayer = {
userName,
age,
surname,
isAdmin: false
};
}
console.log(defaultPlayer);
When you omit a property value, it looks for a variable with the same name as the key. For example, {x} is the same as {x: x}. We can use the with() statement to have JavaScript check for properties of an object before looking for variables. Instead of a normal object, we'll define a Proxy that returns '' for any variables that it doesn't see in the local environment (by testing for an exception when evaling their name).
☠️ This is an obscene hack with nasty edge cases. Never use it. ☠️
When the service requires an object like in method to change name:
{
user: {
data: {
name: string
}
}
}
but, I have object like
{
user: {
age: 18,
height: 150,
weight: 80
data: {
name: 'John',
surname: 'Juhn'
}
}
}
How do I prepare for an object to be compatible with service?
remove unwanted properties with a copy of an object in order not to lose data?
eg.
export function removeUnnecessaryProperty(object: Object, ...necessaryKeys: string[]) {
Object.keys(object).forEach(key => {
if (necessaryKeys.indexOf(key) < 0) { delete object[key]; };
});
}
let objectToRequest = JSON.parse(JSON.stringify(object));
removeUnnecessaryProperty(objectToRequest.data, 'name');
removeUnnecessaryProperty(objectToRequest, 'data');
create a new object only with the required properties?
eg.
export function createObjectWithProperty(object: Object, ...Keys: string[]) {
let newObject: Object = {};
Keys.map(key => {
newObject[key] = object[key];
});
return newObject
}
let objectToRequest = {data: undefined}
objectToRequest.data = createObjectWithProperty(object.data, 'name');
or send an object with additional properties?
Of course you need to pass the exact number of properties.
Why? Don't pass any data that your service doesn't require. Besides this if an hacker can catch your data, he/she will get only that data, not all data.
Think a case when I need to pass only user's name to my Web Api. But my object contains also the email. Why the hacker need also get the email? With name he can't do anything, but with mail he can do many other things.
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);