How can I wrap the value of json with curly braces? - javascript

Let say I have json like this (use JSON.stringify)
{ name: 'Bill', lastname: 'Smith'}
And I want the value wrapped with curly braces like this
{ name: { value: 'Bill' }, lastname: { value: 'Smith'} }
So any idea to do like this using javascript or lodash?

I'd use Object.entries on the input, map to a nested object, then call Object.fromEntries to transform it back again:
const input = { name: 'Bill', lastname: 'Smith'};
const newObj = Object.fromEntries(
Object.entries(input).map(
([key, value]) => ([key, { value }])
)
);
console.log(newObj);
Object.fromEntries is a pretty new method, so for older browsers, either include a polyfill or use something like .reduce instead:
const input = { name: 'Bill', lastname: 'Smith'};
const newObj = Object.entries(input).reduce(
(a, [key, value]) => {
a[key] = { value };
return a;
},
{}
);
console.log(newObj);

You can loop through the keys of the object using for...in and update it like this:
const input = { name: 'Bill', lastname: 'Smith'};
for (const key in input) {
input[key] = { value: input[key] }
}
console.log(input)
If you don't want to mutate the input and want to create a new object, then create another object and update it:
const input = { name: 'Bill', lastname: 'Smith'},
output = {}
for (const key in input) {
output[key] = { value: input[key] }
}
console.log(output)

You can use lodash's _.mapValues() to return a new object with transformed values:
const object = { name: 'Bill', lastname: 'Smith'};
const result = _.mapValues(object, value => ({ value }));
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

Related

JavaScript: How to merge two object but keep only the property of the second object if it exists?

This code merges two objects. How to amend it so that a property is discarded of the first object if it exists in the second object?
E.g. object 1 has this property:
surname: {
test: '123',
other: '124'
},
and object 2:
surname: {
test: '124'
},
The merge would use the property of object 2 (other: '124' would not exist in the merged result)
function merge(...objects) {
function m(t, s) {
Object.entries(s).forEach(([k, v]) => {
t[k] = v && typeof v === 'object' ? m(t[k] || {}, v) : v;
});
return t;
}
return objects.reduce(m, {});
}
var obj1 = {
name: '112',
surname: {
test: '123',
other: '124'
},
age: 151,
height: '183',
weight: 80
},
obj2 = {
name: '114',
surname: {
test: '124'
},
age: 151,
height: 184,
weight: 81
},
result = merge(obj1, obj2);
console.log(result);
const a = { x:1, y:2}
const b = { x:5 }
const c = { ...a, ...b }
console.log(c)
// Answer will be {x: 5, y: 2}
You seem to want to do a shallow merge but with recreated objects to avoid references to the original ones.
A quick way for this is to use Object.assign for the merge, and then do a json stringify/parse (with the limitations of what JSON supports) to clone the result and lose the references (you could alternatively use structuredCloned if your environment/browser supports it).
So something like
function merge(...objects) {
const shallowClone = Object.assign({}, ...objects);
const asString = JSON.stringify(shallowClone);
const asObject = JSON.parse(asString);
return asObject;
}

TypeScript array.map() with dynamic keys

I have an array of Foo called fooArray, but I would like to map() the array to only contain the “key: value” pairs which are defined in arrayOfKeys.
class Foo {
id: number;
name: string;
age: number;
constructor(id: number, name: string, age: number) {
this.id = id;
this.name = name;
this.age = age;
}
}
let fooArray: Foo[] = [
new Foo(1, 'Foo', 20),
new Foo(2, 'Bar', 21),
new Foo(3, 'MyFoo', 20)
];
//The keys I would like to select from Foo.
const arrayOfKeys: (keyof Foo)[] = ['name', 'age'];
I do not know what to do to get the desired result below:
// The result is a copy of 'fooArray', but the objects only
// contain the keys (and their values) defined in 'arrayOfKeys'.
[
{ name: 'Foo', age: 20 },
{ name: 'Bar', age: 21 },
{ name: 'MyFoo', age: 20 }
]
You can probably do this by creating a new object in the map and returning it?
Something like
const fooArrayWithLimitedKeys = fooArray.map(item => arrayOfKeys.reduce(
(accumulator, key) => {
accumulator[key] = item[key]
return accumulator
}, {})
)
it can also be written without reduce like follows:
const fooArrayWithLimitedKeys = fooArray.map(item => {
const returnValue = {}
arrayOfKeys.forEach(key => {
returnValue[key] = item[key]
})
return returnValue;
})
Considering you simply want to modify each item of your array of objects to only contain desired keys, you may go like that:
const src = [
{ id: 1, name: 'Foo', age: 20 },
{ id: 2, name: 'Bar', age: 21 },
{ id: 3, name: 'MyFoo', age: 20 }
]
const keys = ['name', 'age']
const result = src.map(item => Object.assign(
...keys.map(key => ({[key]: item[key]}))
))
console.log(result)
You can use Object Destructuring, inside a map function, something like that.
class Foo {
id: number;
name: string;
age: number;
constructor(id: number, name: string, age: number) {
this.id = id;
this.name = name;
this.age = age;
}
}
let fooArray: Foo[] = [
new Foo(1, 'Foo', 20),
new Foo(2, 'Bar', 21),
new Foo(3, 'MyFoo', 20)
];
const arrayOfKeys: (keyof Foo)[] = ['name', 'age'];
const result = fooArray.map(element =>(({name,age}) => ({name,age}))(element));
console.log(result);
First of all: if you don't really need to reassign fooArray further in code I would recommend it be a const.
Then you can add something like pick function, if you don't want to use lodash or underscore:
function pick(object: any, keys: string[]) {
return keys.map((key: string) => ({ [key]: object[key] }))
}
And use it like this:
const res = fooArray.map(el => pick(el, arrayOfKeys))
Your can create generic function which will accept target array and array of fields to be extracted. You should loop on desired fields and save them to result object. Heres solution in TypeScript:
Generic function
function mapToLimitedProps<A, Keys extends (keyof A)[]>(arr: A[], keys: Keys) {
return arr.map(item => {
let result: Partial<{
[key in Keys[number]]: A[key]
}> = {};
keys.forEach(key => {
result[key] = item[key];
})
return result;
})
}
Usage
mapToLimitedProps(fooArray, arrayOfKeys);

method for copying into a new object while changing one or more proerties

I have an object:
const obj = {
name: "foo",
other: "something"
}
Creating a new object based on my object (a shallow copy) I would use the spread operator and then changing name on my new object:
const newObj = {...obj}
newObj.name = "bar";
But recently I ran into syntax that also does the trick:
const newObj = {
...obj, name: "bar"
}
How does this work and what is it called?
The two methods, you specified are equivalent. In the second method
const newObj = {
...obj, name: "bar"
}
It creates anothey key value pair with key=name and value="bar", since 1 key cannot have multiple values (if its not an array). It overtires the previous value. Code in the second method is equivalent to
const newobj = {
name: "foo",
other: "something",
name: "bar"
}
This is how spread operator works, It allows allows an iterable to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected.
This explains what it does. I hope it helps.
In addition to creating shallow copies, spread allows you to merge objects. The latter will override the former. For example:
const obj = { name: 'jane', age: 22 }
const merged = { ...obj, age: 21 } // { name: 'jane', age: 21 }
const obj = { name: 'jane', age: 22 }
const merged = { ...obj, hasDog: true } // { name: 'jane', age: 22, hasDog: true }
const obj = { name: 'jane', age: 22 }
const obj2 = { age: 19, hasDog: true }
const merged = { ...obj, ...obj2 } // { name: 'jane', age: 19, hasDog: true }
MDN - Spread Syntax

How to convert array to object by key?

Suppose I got this array:
const users =[
{
id:1,
name:'bob',
},
{
id:2,
name:'sally',
},
{
id:3,
name:'bob',
age:30,
}
];
And I want to use any key(in this case 'name' ) to return an object :
{
bob:[
{
id:1,
name:'bob',
},
{
id:3,
name:'bob',
age:30,
}
],
sally:[
{
id:2,
name:'sally',
}
],
}
I tried this:
const go = (A,key) =>{
return A.reduce((o, key) => ({ ...o, [key]:o }), {})
}
export default go;
But this returns:
{ '[object Object]': { '[object Object]': { '[object Object]': {} } } }
If the key is not present omit from the result. It should not mutate the original array though. How can I perform this kind of conversion?
With the approach you have, a new array is not instantiated in case the key is not yet present in the object.
This will work:
const result = users.reduce((a, v) => {
a[v.name] = a[v.name] || [];
a[v.name].push(v);
return a;
}, {});
Complete snippet wrapping this logic in a function:
const users = [{
id: 1,
name: 'bob',
}, {
id: 2,
name: 'sally',
}, {
id: 3,
name: 'bob',
age: 30,
}];
const go = (input, key) => input.reduce((a, v) => {
a[v[key]] = a[v[key]] || [];
a[v[key]].push(v);
return a;
}, {});
console.log(go(users, 'name'));
If you really want to cram it into a one-liner, this will also work, by either spreading the already existing array, or an empty one:
const result = users.reduce((a, v) => ({...a, [v.name]: [...a[v.name] || [], v]}), {});
Complete snippet wrapping this logic in a function:
const users = [{
id: 1,
name: 'bob',
}, {
id: 2,
name: 'sally',
}, {
id: 3,
name: 'bob',
age: 30,
}];
const go = (input, key) => input.reduce((a, v) => ({...a, [v[key]]: [...a[v[key]] || [], v]}), {});
console.log(go(users, 'name'));
You were close but the key attribute in this case was each value (eg: { id: 1, name: 'bob' }) so the string representation is [object Object] which is why all the keys are that. Based off what you said, you want to use key.name as the property and set it's value as [key]. (I renamed key to arr in my example since it's the array value).
So this would be something like { ...o, [arr.name]: [arr] }
Because there can be an existing value, it adds a bit of complexity which is what [...(obj[arr.name] || []), arr] is doing. It's looking up the existing value (or defaulting to an empty array) and spreading those values and adding the new value.
const users = [{
id: 1,
name: 'bob',
},
{
id: 2,
name: 'sally',
},
{
id: 3,
name: 'bob',
age: 30,
}
];
const transform = (input, keyName) => {
return input.reduce((obj, arr) => ({ ...obj,
[arr[keyName]]: [...(obj[arr[keyName]] || []), arr]
}), {})
}
console.log(transform(users, 'name'))
console.log(transform(users, 'id'))

How can you merge objects in array of objects?

I'm looking for the best solution to merge all objects in one array
const arrayOfObjects = [
{name: 'Fred', surname: 'Shultz'}, {name: 'Anne', surname: 'Example'}
];
I want to achieve: {name: ['Fred', 'Anne'], surname: ['Example', 'Shultz']}
What's the best option for that (es6)? Maybe I can do something like that using lodash? Which helpers should I use?
You could reduce the array by iterating the entries and collecting the values, depending of the keys.
const
array = [{ name: 'Fred', surname: 'Shultz' }, { name: 'Anne', surname: 'Example' }],
result = array.reduce((r, o) => {
Object.entries(o).forEach(([k, v]) => (r[k] = r[k] || []).push(v));
return r;
}, Object.create(null));
console.log(result);
You could do it like this:
const arrayOfObjects = [
{name: 'Fred', surname: 'Shultz'}, {name: 'Anne', surname: 'Example'}
];
const result = {};
arrayOfObjects.forEach(item => {
Object.keys(item).forEach(key => {
if (!result[key]) {
result[key] = [];
}
result[key].push(item[key]);
});
});
console.log(result);
easy with lodash:
grouped = _.mapValues(arrayOfObjects[0],
(val, key) => _.map(arrayOfObjects, key))
pure es6
let grouped = {};
for (let obj of arrayOfObjects)
for (let [key, val] of Object.entries(obj))
grouped[key] = (grouped[key] || []).concat(val)
if the keys differ from item to item, you could use something like this to collect them all:
grouped = _(arrayOfObjects)
.flatMap(_.entries)
.groupBy(0)
.mapValues(x => _.map(x, 1))
.value()
You can use lodash's mergeWith like so:
const result = _.mergeWith({}, ...arrayOfObjects, (value, objValue) =>
(value || []).concat(objValue)
);
Example:
const arrayOfObjects = [
{name: 'Fred', surname: 'Shultz'}, {name: 'Anne', surname: 'Example'}
];
const result = _.mergeWith({}, ...arrayOfObjects, (value, objValue) =>
(value || []).concat(objValue)
);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
Short way with array reduce:
const arrayOfObjects = [
{name: "name1", surname: "surname1"}, {name: 'Anne', surname: 'Example'}, {name: 'name3', surname: 'Example3'}
];
/*
{name: ['Fred', 'Anne'], surname: ['Example', 'Shultz']}
*/
var result = arrayOfObjects.reduce((obj,current)=>{
(obj['name'] = obj['name']||[]).push(current.name);
(obj['surname'] = obj['surname']||[]).push(current.surname);
return obj;
},{});
console.log(result);
Don't make it any more complicated than it needs to be:
const arrayOfObjects = [
{name: 'Fred', surname: 'Shultz'},
{name: 'Anne', surname: 'Example'}
];
const result = {name:[], surname:[]};
for (const obj of arrayOfObjects)
for (const prop in result)
result[prop].push(obj[prop]);
I will assume that you statically know the property names that your result should have - one can't really do it dynamically anyway as that wouldn't work properly for an empty input array.
Here is a lodash approach
_(input).flatMap(_.entries).groupBy(0).mapValues(v => _.map(v, 1)).value()
var input = [
{name: 'Fred', surname: 'Shultz'}, {name: 'Anne', surname: 'Example'}
];
var res = _(input).flatMap(_.entries).groupBy(0).mapValues(v => _.map(v, 1)).value()
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
This will take care if the objects doesn't have exactly same key sets
If the arrayOfObjects is set on these 2 props then it is as simple as:
const data = [{ name: 'Fred', surname: 'Shultz' }, { name: 'Anne', surname: 'Example' }]
const r = data.reduce((r,c) =>
(r.name.push(c.name), r.surname.push(c.surname),r), {name:[], surname:[]})
console.log(r)
One reduce with an accumulator of {name:[], surname:[]} to be filled.
If you need to be more generic and work for any set of objects:
const data = [{
name: 'Fred',
surname: 'Shultz'
},{
name: 'Anne',
surname: 'Example'
},{
name: 'John',
position: 'Dev' // <--- Notice different prop
}]
const result = data.reduce((r,c) =>
(Object.keys(c).map(k => r[k] = [...r[k] || [], c[k]]), r), {})
console.log(result)
Again is just a reduce with Object.keys to do the job.
Note both approaches utilize ES6 arrow functions, array destricturing and (for the 2nd one) combining multiple operations via enclosing them in parentheses (op1,op2)
This is one abroach of implementation details, written in fairly easy to understand and readable manner.
https://codesandbox.io/s/r7x16j950n
const arrayOfObjects = [
{ name: "Fred", surname: "Shultz" },
{ name: "Anne", surname: "Example" }
];
let obj = {};
arrayOfObjects.forEach(row => {
Object.keys(row).forEach(key => {
obj[key] = !obj[key]
? [row[key]]
: [...obj[key], row[key]];
});
});
console.log(obj);
The following should work - uses a few ES6 helpers, but the key is Array#reduce which is in ES5.
const result = arrayOfObjects.reduce((acc, obj) => {
for (let key in obj) {
if (key in acc) {
acc[key].push(obj[key]);
}
else {
acc[key] = [obj[key]];
}
}
return acc;
}, {});
Without any library
const mergeObjectInArray=(input)=>{
const myObj={};
Object.keys(input[0]).forEach(key=>myObj[key]=input.map(inp=>inp[key]));
return myObj;
}
with pure javascript
var myInput = [{ a: 1, b: 2, c: 3 }, { a: 2, b: 4, c: 6 }, { a: 7, b: 8, c: 9 }];
var myArray = [];
var myObject = {};
function isArray(a){
return Object.prototype.toString.call(a) === '[object Array]' ;
}
for (var i = 0; i < myInput.length; i++) {
for (var key in myInput[i]) {
if (myInput[i].hasOwnProperty(key)) {
if (myArray.indexOf(key) === -1) {
myArray.push(key);
myObject[key] = myInput[i][key];
} else {
if (myObject.hasOwnProperty(key)) {
newary = [];
if (isArray(myObject[key])) {
for (var i = 0; i < myObject[key].length; i++) {
newary.push(myObject[key][i]);
}
} else {
newary.push(myObject[key]);
}
newary.push(myInput[i][key]);
myObject[key] = newary;
}
}
}
}
}
console.log(myObject);

Categories