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);
Related
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
Please read fully. Do not mark it duplicate until you really find it. I have an array of objects like below.
const filterParams = [
{ 'waterfront_type[]': 'Cove' },
{ 'area[]': 'Applehead Island' },
{ 'waterfront_type[]': 'Channel' },
{ 'waterfront_type[]': 'Open Water' },
{ baths: '0 - 14' },
{ price: '0.00 - 9,876,100.00' }
];
I need to loop over this and add this to form data. I'm doing like below.
filterParams.map(param => {
return formData.append(param.key, param.value);
});
Expected: param.key is waterfront_type[] and param.value is Cove. so internally it should be formData.append('waterfront_type[]', 'Cove');
Getting: both undefined. formData.append(undefined, undefined);
You can use Object.entries(), and array destructuring to get the key, and the value.
Note: the default = [] handles the case of empty objects.
filterParams.map(param => {
const [[key, value] = []] = Object.entries(param);
return formData.append(key, value);
});
If you just need to call formData(key, value), and you don't care about the returned values (if any), use Array.forEach() instead of Array.map():
filterParams.forEach(param => {
const [[key, value] = []] = Object.entries(param);
formData.append(key, value);
});
param variable passed to .map function is a javascript object and you cannot access its keys by using just object.key. Instead do:
filterParams.map(param => {
var keys = Object.keys(param);
return formData.append(keys[0], param[keys[0]]);
});
Here, we are getting all the keys of the param object and get the first (0th index) as the key you require (assuming that your array have objects with one key).
So you want to put all dictionary entrySet into a FormData:
filterParams.forEach(param => {
Object.keys(param).forEach(function(key) {
let value = param[key];
formData.append(key, value);
});
});
It will produce the output of:
["waterfront_type%5B%5D=Cove",
"area%5B%5D=Applehead%20Island",
"waterfront_type%5B%5D=Channel",
"waterfront_type%5B%5D=Open%20Water",
"baths=0%20-%2014",
"price=0.00%20-%209%2C876%2C100.00"]
you can use javascript filters
filterParams = filterParams.filter(function(itm){
Object.keys(itm).forEach(function(key, value){
return formData.append(key, value);
})
})
I'm trying to nest-group an array of objects.
The function provided by this gist almost works as intended and uses lodash as basis:
https://gist.github.com/joyrexus/9837596
const _ = require('lodash');
function nest(seq, keys) {
if (!keys.length) return seq;
let [first, ...rest] = keys;
return _.mapValues(_.groupBy(seq, first), value => nest(value, rest));
}
This recursively,
However, there are two problems I face.
if a parameter is set to null or undefined, it is used as a group, instead the
an optional object attribute should be used as the final object key, so there are only objects, no arrays. This attribute always has to be unique in order to work correctly.
Is it possible to combine or extend the existing nest function to solve the above points?
The pros of this method is, that instead of the keys, I can also use an array of functions (p => p.parameterGroup1) to return the parameter. So instead of a last optional parameter, I could also use p => p.parameterGroup1 ? p.parameterGroup1 : p.label
I prepared a little test, to give you a better idea of what is expected:
test('nest array of objects by groups as keys, stopping at null and using a final label param', t => {
let properties = [
{
parameterGroup1: 'first',
parameterGroup2: 'second',
parameterGroup3: 'third',
label: 'A'
},
{
parameterGroup1: 'first',
parameterGroup2: 'second',
parameterGroup3: null,
label: 'B'
},
{
parameterGroup1: 'a',
parameterGroup2: 'b',
parameterGroup3: undefined,
label: 'C'
},
]
let expected = {
first: {
second: {
third: {
A: {
parameterGroup1: 'first',
parameterGroup2: 'second',
parameterGroup3: 'third',
label: 'A'
}
},
B: {
parameterGroup1: 'first',
parameterGroup2: 'second',
parameterGroup3: null,
label: 'B'
}
}
},
a: {
b: {
C: {
parameterGroup1: 'a',
parameterGroup2: 'b',
parameterGroup3: undefined,
label: 'C'
}
}
}
}
let grouped = nest(properties, ['parameterGroup1', 'parameterGroup2', 'parameterGroup3'], 'label')
t.deepEqual(grouped, expected)
})
Thank you in advance!
Here is a way to do it in vanilla js. We construct the result object by reduceing the array seq: For each object obj in the array seq, we walk the result object level by level using the values from obj of the keys from keys. If the value is null or undefined, we skip (won't go down another level). If the value exist we go down a level, creating a level (object) if it doen't already exist. We do that repeatedly using a reduce on the keys array untill we find the leaf object (last level), to which we assign our current object under the key obtained evaluating obj[last]:
function nest(seq, keys, last) {
return seq.reduce((result, obj) => {
// First we find the (last level) object to which we will assign our current object to, as a child
let lastLevel = keys.reduce((res, key) => { // for each key in keys
let value = obj[key]; // get the value from our current object obj for that key key
if(value == null) return res; // if the value is null or undefined, skip
if(res[value]) return res[value]; // if the level for value exists return it
return res[value] = {}; // if it doesn't, create a new level, assing it to result and return it
}, result);
// then we assign it using the value of the key last
lastLevel[obj[last]] = obj; // we found the last possible level, assign obj to it under the key obj[last]
return result;
}, {});
}
Example:
function nest(seq, keys, last) {
return seq.reduce((result, obj) => {
let lastLevel = keys.reduce((res, key) => {
let value = obj[key];
if(!value) return res;
if(res[value]) return res[value];
return res[value] = {};
}, result);
lastLevel[obj[last]] = obj;
return result;
}, {});
}
let properties = [{parameterGroup1: 'first',parameterGroup2: 'second',parameterGroup3: 'third',label: 'A'},{parameterGroup1: 'first',parameterGroup2: 'second',parameterGroup3: null,label: 'B'},{parameterGroup1: 'a',parameterGroup2: 'b',parameterGroup3: undefined,label: 'C'}];
let grouped = nest(properties, ['parameterGroup1', 'parameterGroup2', 'parameterGroup3'], 'label');
console.log(grouped);
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)
}
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??