Combine array of objects into a single object with a nested object - javascript

I'm trying to turn this data
[
{
key: 'myvar',
value: 'my value',
global: false,
},
{
key: 'foo',
value: 'bar',
global: false,
},
{
key: 'featureEnabled',
value: true,
global: true,
accountId: 123,
},
]
into this
{
myvar: 'my value'
foo: 'bar'
123: { 'featureEnabled': true }
}
I was thinking something along the lines of something like below but this will return undefined for objects that don't have an accountId property. I thought there might be a way to use an if statement here.
const globalResponse = Object.assign({}, ...getPreference.map(o => ({ [o.accountId]: { [o.key]: o.value } })))

You can use of reduce to build your new object and setup specific triggers for specifics use cases.
function mutateObject(tmpBasis, key, value) {
tmpBasis[key] = value;
return tmpBasis;
}
const ret = [{
key: 'myvar',
value: 'my value',
global: false,
},
{
key: 'foo',
value: 'bar',
global: false,
},
{
key: 'featureEnabled',
value: true,
global: true,
accountId: 123,
},
{
key: 'otherKey',
value: true,
global: true,
accountId: 123,
},
{
key: 'featureEnabled',
value: true,
global: true,
accountId: 512,
},
].reduce((tmp, {
key,
value,
global,
accountId,
}) => ({
...tmp,
// If we are in global case, we either create an empty object containing the new key
// or we add the new key inside of the existing object
[global ? accountId : key]: global ? mutateObject(tmp[accountId] || {}, key, value) : value,
}), {});
console.log(ret);

You can use reduce() with a condition inside like below
var myJSON = [{
key: 'myvar',
value: 'my value',
global: false,
},
{
key: 'foo',
value: 'bar',
global: false,
},
{
key: 'featureEnabled',
value: true,
global: true,
accountId: 123,
},
];
let newJSON = myJSON.reduce((target, item) => {
!!item.accountId ? target[item.accountId] = { [item.key]: item.value } : target[item.key] = item.value;
return target;
}, {});
console.log(newJSON);

Related

Map the object using javascript and get expected structure object

I want to map below mentioned object...
Input object:
const rules = {
public: [
{ label: 'View', value: 'View' },
{ label: 'Create', value: 'Create' },
{ label: 'Delete', value: 'Delete' },
{ label: 'Update', value: 'Update' },
{ label: 'ChangeLayout', value: 'ChangeLayout' }
],
user: [
{ label: 'View', value: 'View' },
{ label: 'Create', value: 'Create' },
{ label: 'Delete', value: 'Delete' },
{ label: 'Update', value: 'Update' },
{ label: 'ChangeLayout', value: 'ChangeLayout' }
]
};
Expected output is:
const result = {
Public: { "View": true, "Create": false, "Delete": false, "Update": false, "ChangeLayout": false },
User: { "View": true, "Create": true, "Delete": true, "Update": true, "ChangeLayout": true }
};
Actually i try some method but cant getting expected output
Thanks to advance please help anyone...
Please try to use below code
const rules = [
{ label: 'View', value: 'View' },
{ label: 'Create', value: 'Create' },
{ label: 'Delete', value: 'Delete' },
{ label: 'Update', value: 'Update' }
];
let outPut = {};
rules.forEach(item => {
outPut[item.value] = true;
})
console.log(outPut);
Your callback function is just returning a string, so map() is creating an array of strings, not an object.
The callback function should return an array containing the key and value. Then you can use Object.fromEntries() to turn that into the desired object.
const rules = {
public: [
{ label: 'View', value: 'View' },
{ label: 'Create', value: 'Create' },
{ label: 'Delete', value: 'Delete' },
{ label: 'Update', value: 'Update' },
{ label: 'ChangeLayout', value: 'ChangeLayout' }
],
user: [
{ label: 'View', value: 'View' },
{ label: 'Create', value: 'Create' },
{ label: 'Delete', value: 'Delete' },
{ label: 'Update', value: 'Update' },
{ label: 'ChangeLayout', value: 'ChangeLayout' }
]
};
const test = Object.fromEntries(Object.entries(rules).map(
([owner, rules]) => [owner, Object.fromEntries(
rules.map(rule => [rule.value, true]))]));
console.log(test);
To create a single object from your array, you can use a reduce operation, assigning the resulting key name via computed property names.
const rules = [{"label":"View","value":"View"},{"label":"Create","value":"Create"},{"label":"Delete","value":"Delete"},{"label":"Update","value":"Update"}]
const keyFrom = "value"
const t0 = performance.now()
const result = rules.reduce((obj, rule) => ({
...obj,
[rule[keyFrom]]: true
}), {})
const t1 = performance.now()
console.log(result)
console.log(`Operation took ${t1 - t0}ms`)
I wasn't sure which property should provide the key name in your final object so I made it configurable via the keyFrom variable.

Merge two objects and enhance result by adding a new key

I have two objects at the moment (one JSON Schema and one as a response from our API) which I want to merge for better mapping and usage.
The Schema looks like this:
// schema
{
key: {
description: "foo",
properties: {
values: {
title: "foo",
type: "Array"
},
type: "string"
},
type: "object"
},
foo: {
title: "title",
description: "bar"
},
bar: {
title: "title",
description: "who"
}
}
And my response object is similar to this:
// response
{
key: {
values: [0, 1]
type: "point"
},
foo: null,
bar: "some string"
}
I would simply like to merge those two objects, but using const mergedObject = {...schema, ...response} would cause overriding the values.
So my desired outcome would contain a new object prop called value or something which contains the values of the response object:
{
key: {
value: {
values: [0, 1],
type: "point",
},
description: "foo",
properties: {
values: {
title: "foo",
type: "Array"
},
type: "string"
},
type: "object"
},
foo: {
value: null,
title: "title",
description: "bar"
},
bar: {
value: "some string",
title: "title",
description: "who"
}
}
Is this doable using the spread operator? I couldn't find a decent solution here since lodashs assign or assignIn don't provide that functionality either.
I tried this function as well:
function merge (...objs) =>
[...objs].reduce(
(acc, obj) =>
Object.keys(obj).reduce((a, k) => {
acc[k] = acc.hasOwnProperty(k) ? [].concat(acc[k]).concat(obj[k]) : obj[k];
return acc;
}, {}),
{}
);
but it gives me
{
bar: [
{
title: "title",
description: "who"
},
"some string",
],
foo: [
{
title: "title",
description: "bar",
},
null
],
key: [
{
description: "foo",
properties: {
values: {
title: "foo",
type: "Array"
},
type: "string"
},
type: "object"
},
{
values: [0, 1]
type: "point"
}
]
}
which is also not what i want.
Any help is appreciated, thanks!
You can combine Object.keys(...) and Spread Operator:
const objA = {
key: {
description: "foo",
properties: {
values: {
title: "foo",
type: "Array"
},
type: "string"
},
type: "object"
},
foo: {
title: "title",
description: "bar"
},
bar: {
title: "title",
description: "who"
}
}
const objB = {
key: {
values: [0, 1],
type: "point"
},
foo: null,
bar: "some string"
}
function mergeObjects (objectA, objectB) {
const mergedObject = {};
Object.keys(objectA).forEach((key) => {
mergedObject[key] = {
...objectA[key],
value: typeof objectB[key] === 'object' && objectB[key] !== null
? { ...objectB[key] }
: objectB[key]
}
})
return mergedObject;
}
console.log(mergeObjects(objA, objB));
You need to look into this
const data = {
key: {
description: 'foo',
properties: {
values: {
title: 'foo',
type: 'Array',
},
type: 'string',
},
type: 'object',
},
foo: {
title: 'title',
description: 'bar',
},
bar: {
title: 'title',
description: 'who',
},
};
const res = {
key: {
values: [0, 1],
type: 'point',
},
foo: null,
bar: 'some string',
};
const output = { ...data };
Object.keys(res).forEach((r) => {
const isPresent = !!(data[r]);
if (isPresent) {
const responseValues = res[r];
output[r] = { responseValues, ...data[r] };
} else {
output[r] = res[r];
}
});
console.log(output);

How to push object in specified index position in array?

I have an array.
var tableHeader = [
{
key: 1,
value: 'inputname',
defaultChecked: true,
columnName: 'input.name',
}, {
key: 3,
value: 'callname',
defaultChecked: true,
columnName: 'call.name',
}, {
key: 4,
value: 'rank',
defaultChecked: true,
columnName: 'call.rank',
}, {
key: 5,
value: 'threshold',
defaultChecked: true,
columnName: 'call.threshold',
}, {
key: 9,
value: 'matchname',
defaultChecked: true,
columnName: 'match.name',
},
]
My requirement: I will remove the object having key 3. While I will push the same object to the array it will push to same position as before. The same will happen if I do for other objects.
I just tried in Typescript I dnt know I much it helps to you,I added empty string in removed object place, after that I replaced with original object
let removedObj, removedIndex: any;
this.tableHeader.forEach((ele, i) => {
if (ele.key == 3) {
removedObj = ele; removedIndex = i;
this.tableHeader.splice(i, 1, '');
}
});
this.tableHeader.splice(removedIndex, 1, removedObj);
console.log(this.tableHeader);
to replace the array element use:
TheNewObject = { key: 9,
value: 'matchname',
defaultChecked: true,
columnName: 'match.name',};
tableHeader[3] = TheNewObject;
just like that ,
and to search for object Index you can use the following method :
function getObjectIndex(skey)
{
for (i = 0; i < tableHeader.length; i++) {
obj = tableHeader[i];
if (obj.hasOwnProperty('key') && obj.key == skey) {
return i;
}
}
}
This looks like two distinct problems, one is to filter out an element inside an array by its property, and the second (and slightly trickier to push a new element back in the same place if it has the same key). I think your best bet is to leave .push alone, and instead look into Array.filter and Array.sort after you add a new element (to restore order), Like this:
var tableHeader = [{
key: 1,
value: 'inputname',
defaultChecked: true,
columnName: 'input.name',
}, {
key: 3,
value: 'callname',
defaultChecked: true,
columnName: 'call.name',
}, {
key: 4,
value: 'rank',
defaultChecked: true,
columnName: 'call.rank',
}, {
key: 5,
value: 'threshold',
defaultChecked: true,
columnName: 'call.threshold',
}, {
key: 9,
value: 'matchname',
defaultChecked: true,
columnName: 'match.name',
}, ]
console.log('Original', tableHeader)
//Filter out {key:3}
tableHeader = tableHeader.filter(function(e) {
return e.key !== 3
})
console.log('Filtered', tableHeader)
tableHeader.push({
key: 3,
value: 'callname',
defaultChecked: true,
columnName: 'call.name',
})
tableHeader.sort(function(a, b) {
return a.key - b.key
})
console.log('Resorted', tableHeader)

Extend two objects with no deep copy but without overwrite array of objects

I've two objects and I would like to join in one just by overwriting the same values.
var objOne = { current: 1, sort: { 'foo' : 'bar' }, search: [ { name: 'a', value: 1 }, { name: 'b', value: 2 } ] };
var objTwo = { sort: { 'john' : 'doe' }, search: [ { name: 'a', value: 3 } ] };
I tried with $.extends without deep copy and works well for all elements except for array objects.
var objAll = $.extend( {}, objOne, objTwo );
objAll -> { current: 1, sort: { 'john' : 'doe' }, search: [{ name: 'a', value: 3 }] };
My goal would be to get this
objAll -> { current: 1, sort: { 'john' : 'doe' }, search: [{ name: 'a', value: 3 },{ name: 'b', value: 2 }] };
Overwrite only the array of objects with the same name.
How can I do this? thanks
This can be done using ES6 Proxies, using set trap in the handler.
var objOne = { current: 1, sort: { 'foo' : 'bar' }, search: [ { name: 'a', value: 1 }, { name: 'b', value: 2 } ] };
var proxy = new Proxy(objOne, {
set(trapTarget, key, value, receiver) {
// not altering the existing array properties
if (trapTarget.hasOwnProperty(key) && Array.isArray(trapTarget[key])) {
return Reflect.set(trapTarget, key, trapTarget[key], receiver)
}
return Reflect.set(trapTarget, key, value, receiver);
} });
Object.assign(proxy,{ sort: { 'john' : 'doe' }, search: [ { name: 'a', value: 3 } ] });
console.log(objOne);
JsFiddle Link: https://jsfiddle.net/8n3c8hs0/

How to find the first property that is an array in an object?

I'm creating a function that loops through an array like this:
schema: [{
name: 'firstRow',
fields: [{
name: 'name',
text: 'Name',
type: 'text',
col: 12,
value: ''
}]
}, {
And returns a callback with the values of the objects:
eachDeep (array, callback) {
array.forEach(item => {
item.fields.forEach(field => {
callback(field)
})
})
},
As you can see the item.fields.forEach part is harcoded. How can I modify the function so it detects the first property that it's an array and loop through it? (e.g. in this case that property is fields).
To find whether a property of an object is an array or not you can also use this one:
//let item be your object's property
if(typeof item == "object" && item.length > 0){
//do whatever if it is an array
}
You can check if the field is not an array or not, if so loop it, otherwise do something else with it.
var data = [{
name: 'firstRow',
fields: [{
name: 'name',
text: 'Name',
type: 'text',
col: 12,
value: ''
}]
}, {
name: 'firstRow',
fields: [{
name: 'name',
text: 'Name',
type: 'text',
col: 12,
value: ''
}]
}];
eachDeep (array, callback) {
array.forEach(item => {
// loop through each property again
item.forEach(prop => {
// if property is an array
if (prop instanceof Array) {
prop.forEach(field => callback(field));
} else {
// property is not an array
// do something else
}
})
})
},
var big_array =
[
{
name: 'firstRow',
fields: [{
name: 'name',
text: 'Name',
type: 'text',
col: 12,
value: ''
}]
}
];
for (let item of big_array)
{
for (let key in item)
{
if (Array.isArray(item[key]) )
{
console.log('this is an array do something:', key);
}
}
}
You could check using Array.isArray()
If the goal is to find the first array property you can do the following. Using ES6.
const schema = [{
name: 'firstRow',
fields: [{
name: 'name',
text: 'Name',
type: 'text',
col: 12,
value: ''
}]
}]
let firstArr;
schema.forEach(item => {
firstArr = Object.keys(item).filter(k => Array.isArray(item[k]))[0];
})

Categories

Resources