modify a property of an object in the functional way - javascript

In javascript programming in the functional way is a great benefit. I'm trying to modify a property of an object contained in an array of objects in the functional way that means that the item that is the object passed in the map function cannot be modified. If I do something like this:
const modObjects = objects.map((item) => {
item.foo = "foo" + 3;
return item;
});
this is not functional because item is modified inside the function. do you know any other approach to this problem?

A new (ES6) way that is really immutable and in the spirit of functional programming:
// A. Map function that sets obj[prop] to a fixed value
const propSet = prop => value => obj => ({...obj, [prop]: value})
// B. Map function that modifies obj.foo2 only if it exists
const foo2Modify = obj =>
obj.hasOwnProperty('foo2') ? {...obj, foo2: 'foo ' + obj.foo2} : obj
// Usage examples of A and B
const initialData = [{'foo': 'one'}, {'foo2': 'two'}, {'foo3': 'three'}]
const newData1 = initialData.map(propSet('foo2')('bar')) // Set value
const newData2 = initialData.map(foo2Modify) // Use a modify function
console.log(initialData) // Initial data should not change
console.log(newData1) // Each object should contain the new fixed foo2
console.log(newData2) // Modify foo2 only if it exists in initial data

You could use Object.assign to create a copy of the item obj and return that from the map callback.
Object.assign()
The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.
Here is an example
let data = [
{"foo": "one"},
{"foo": "two"},
{"foo": "three"}
]
let newData = data.map( item => {
let itemCopy = Object.assign({}, item);
itemCopy.foo = "foo " + item.foo;
return itemCopy;
})
console.log(data)
console.log(newData)

You can also do it like this:
const modObjects = objects.map((item) => {
return { ...objects, foo: "foo" + 3; };
});
The reason that this: objects.map((item) => { ...destSchema, foo: "foo" + 3; }); doesn't work is that they made it this way to make the JS interpreter understand whether it is a scope or an object. You MUST use return

In modern JavaScript you can use spread operator on object inside of an object literal for this:
const modObjects = objects.map(
item => ({...item, foo: item.foo + 3})
)
Notice parentheses () around object literal. They are needed to disambiguate object literal {} from code block {} in lambdas. Another way is to use code block with return:
const modObjects = objects.map(
item => { return {...item, foo: item.foo + 3} }
)

I have extended #Dimitrios Tsalkakis answer to change property with a callback function.
Example: https://repl.it/#robie2011/ts-set-property-functional
Typescript:
function mapProperty<T>(prop: string){
return (cb: (propValue: any) => any) => (obj: any) => ({...obj, [prop]: cb(obj[prop])}) as (T)
}

Related

Javascript Function: Use Variable instead of single Values

I found here a script. It works fine. But now, I want to use a Variable instead of single values.
Here the original script:
const customData = {
"func":"bri",
"oid":"ID",
"onVal":1,
"offVal":0,
"...":"..."
}
const getSubset = (obj, ...keys) => keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {});
const Light.bri = getSubset(customData, "oid", "onVal", "offVal");
Result (OK):
bri: {
offVal: 0,
oid: "objekt-ID",
onVal: 1
},
Now I want to do define the keys in a variable, ideally as a object. But this do not work.
const params = {bri: "oid, onVal, offVal"};
const Light.bri = getSubset(customData, params.bri);
Result (NOK):
bri: {
oid, onVal, offVal: undefined
},
description: "Deckenspots"
}
what changes do I have to make?
Define the bri property as an array of strings. That way you can use the spread syntax (...) to pass the strings as individual arguments.
const params = {bri: ["oid", "onVal", "offVal"]}; // bri is now an array.
const Light.bri = getSubset(customData, ...params.bri); // Notice the ... before params.bri

What is the most elegant way to remove an array of properties from an object immutably in JavaScript?

Not sure if I'm trying to accomplish too much here in an elegant way, but let's say I have an object as follows:
const obj = { foo: 'bar', prop: 'str', hi: 'hello' };
And then I have an array of properties I want to remove from that object:
const keys = ['prop', 'hi'];
I'm looking for the best way I can immutably get an output of:
{ foo: 'bar' }
I was looking into using destructuring assignments but I couldn't figure out how to make it work with an array of properties unless it has a static length:
({ [keys[0]]: value, [keys[1]]: value, ...output }) = o;
Maybe there's an alternative way to do the above line but using mapping?
Thanks!
You could iterate the array and use delete to delete each matching keys.
To make a copy of the object you have you could use the spread operator
const keys = ['prop', 'hi'];
const obj = { foo: 'bar', prop: 'str', hi: 'hello' };
const newobj={...obj}
keys.forEach(o=> delete newobj[o])
console.log(obj)
console.log(newobj)
Here is another approach using Object.fromEntries and filter, some
const keys = ['prop', 'hi'];
const obj = { foo: 'bar', prop: 'str', hi: 'hello' };
res=Object.fromEntries(Object.entries(obj).filter(o=>!keys.some(k=>o[0]==k)))
console.log(res)
console.log(obj)
I would use a combination of the Object.fromEntries, Object.entries, and the Array .filter methods. Then this operation becomes quite straightforward:
function removeKeysFromObj(obj, keys) {
return Object.fromEntries(Object.entries(obj).filter(([key]) => !keys.includes(key)));
}
const obj = { foo: 'bar', prop: 'str', hi: 'hello' };
const keys = ['prop', 'hi'];
const newObj = removeKeysFromObj(obj, keys);
console.log("New object:", newObj);
// And note the original is unmodified:
console.log("Original object:", obj);
One way to approach this is to think in the reverse way: map over the keys in the original object and keep the ones not in the list:
function removeKeys(obj, keys) {
return Object.keys(obj)
.filter(k => !keys.includes(k))
.reduce((newObj, k) => {
newObj[k] = obj[k];
return newObj
}, {});
I'd do it like this.
const obj = { foo: 'bar', prop: 'str', hi: 'hello' };
const keys = ['prop', 'hi'];
let result = Object.keys(obj).reduce((current, item) => (keys.includes(item) || (current[item] = obj[item]), current), {});
console.log(result);

Turn array into array of object using map failed

const arr = ["a","b","c"]
arr.map(obj => {obj.id: obj})
I expect arr can become [{id:"a"},{id:"b"},{id:"c"}] but I got error at obj.id within my map, what's my mistake?
You could use parenthesis for the return object and just the variable id, which resolves to an object with the key id.
let arr = ["a", "b", "c"],
result = arr.map(id => ({ id }));
console.log(result);
From MDN Returning object literals:
Returning object literals
Keep in mind that returning object literals using the concise syntax params => {object:literal} will not work as expected.
var func = () => { foo: 1 };
// Calling func() returns undefined!
var func = () => { foo: function() {} };
// SyntaxError: function statement requires a name
This is because the code inside braces ({}) is parsed as a sequence of statements (i.e. foo is treated like a label, not a key in an object literal).
Remember to wrap the object literal in parentheses.
var func = () => ({foo: 1});
you should use extra parenthesis when returning object. so your code will be
const arr = ["a", "b", "c"]
const a = arr.map((obj, i) => ({
[i]: obj
}))
console.log(a)

Adding property using Object assign

I have the following function:
addHrefs = (list) => {
const listWithHrefs = list.map((item) => {
item.href = customFunction(item.name);
return item;
});
return listWithHrefs;
}
The problem here is I am mutating the list object. I want to know how to add a particular (key, value) pair without mutating list. Is it possible with Object.assign? How can I do it?
It is possible with Object.assign.
The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.
You could assign the property value to a new object and return it.
addHrefs = list =>
list.map(item => Object.assign({}, item, { href: customFunction(item.name) }));
You can pass plain object as first parameter to Object.assign(), referenced object at second parameter. Perform action on variable assigned to Object.assign(), return variable
let list = [{
a: 1
}, {
a: 2
}, {
a: 3
}];
let res = list.map(item => {
let o = Object.assign({}, item);
// do stuff with `o`
o.a = o.a + 10;
// return `o`
return o
});
console.log(list, res);

Can one set multiple properties inside an object literal to the same value?

For example, can I do this?:
{
a: b: c: d: 1,
e: 2,
geh: function() { alert("Hi!") }
}
EDIT:
Is there some way I can avoid doing this?:
{
a: 1,
b: 1,
c: 1,
d: 1,
e: 2,
geh: function() { alert("Hi!") }
}
An update to this (in terms of the latest JavaScript abilities) avoiding unwanted defined vars:
{
let v;
var obj = {
"a": (v = 'some value'),
"b": v,
"c": v
};
}
This will mean v won't be defined outside the block, but obj will be.
Original answer
Another way of doing the same thing is:
var v;
var obj = {
"a": (v = 'some value'),
"b": v,
"c": v
};
You could set a line of equality between various properties:
var foo = {};
foo.a = foo.b = foo.c = "Hello";
Or you could just create a method that does the mass-assignment for you:
var foo = {
setValue: function( props, value ) {
while ( props.length ) this[ props.pop() ] = value;
}
}
foo.setValue( [ "a", "b", "c" ] , "Foo" );
You could try this. It's not the syntactic sugar you're looking for (eg. {a,b,c:1, d:2}) but it's another way to do it, although all of these answers are pretty much fine.
(object,fields,value)=>Object.assign(object||{}, ...fields.map(f=>({[f]:value}) ))
Explanation:
(object,fields,value)=>
Takes an object (or falsey value if you want a new object, feel free to rearrange the argument order)
Object.assign(object||{},
Will return an object based on object and it will mutate the object. To disable this, simply add a first argument object literal like this Object.assign({}, object || {}, ...
...fields.map(f=>({[f]:value}) )
Will spread the array of fields mapped to objects as a list of extra arguments to Object.assign. ['a','b'].map(f=>({[f]:value}) ) will give [{a:value}, {b:value}] and f(...[{a:1},{b:1}]) is like f({a:1},{b:1}). Object.assign does the rest :)
There's yet another approach: using a mapping function...
// This will be standard!
if (!Object.fromEntries)
Object.fromEntries = entries => entries.reduce ((o, [key, value]) => ({
...o,
[key]: value
}), {})
const setSameValue = (source, props, value) => ({
...source,
...Object.fromEntries (
props.map (prop => [prop, value])
)
})
// The important part: do what you want with ease!
const output = setSameValue ({}, ['1', '01'], 'string 1')
const obj = { x: 1, y: 'hello' }
const output2 = setSameValue (obj, ['1', '01'], 'string1')
console.log ('output1:', output)
console.log ('output2:', output2)
You could wrap in a closure too, if you didn't want multiple local vars. This syntax seems to be popular (but ugly):
var obj = (function() { var v='some value'; return { a:v, b:v, c:v }; })();
Use for of loop instead.
for (let [key, value] of Object.entries(object_name)) {
object_name[key] = 0; // the value that you want to assign
}
Or yet another way:
{...['a', 'b', 'c', 'd'].reduce((obj,prop)=>({...obj, [prop]: 1}), {}) }
It can be wrapped up pretty neatly by extending the Array prototype:
Array.prototype.ditto = function(v) { return this.reduce((o,p)=>({...o, [p]: v}), {}) }
So now it can be used like this:
{
...['a', 'b', 'c', 'd'].ditto(1),
...['e', 'f'].ditto(2)
geh: function() { alert("Hi!") }
}
Explanation: the .reduce starts off with an empty object {} and for each element prop return an object which is whatever was in the object already ...obj plus a new property with our value 1: [prop]: 1. Then expand these properties into the outer object with the ... at the start.
If you had tons of properties reduce wouldn't be the most efficient, but you could change it to:
Array.prototype.ditto = function(v) { let o = {}; this.forEach(p => o[p] = v); return o; }
More readable and more efficient but less cool??

Categories

Resources