Merge two objects and enhance result by adding a new key - javascript

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);

Related

Return last value with recursion - Javascript

Hi all I have following data:
const section = {
fileds: [
{ id: "some Id-1", type: "user-1" },
{
child: [
{ id: "some Id-2", type: "user-2" },
{ fileds: [{ id: "kxf5", status: "pending" }] },
{ fileds: [{ id: "ed5t", status: "done" }] }
]
},
{
child: [
{ id: "some Id-3", type: "teacher" },
{ fileds: [{ id: "ccfr", status: null }] },
{ fileds: [{ id: "kdpt8", status: "inProgress" }] }
]
}
]
};
and following code:
const getLastIds = (arr) =>
arr.flatMap((obj) => {
const arrayArrs = Object.values(obj).filter((v) => Array.isArray(v));
const arrayVals = Object.entries(obj)
.filter(([k, v]) => typeof v === "string" && k === "id")
.map(([k, v]) => v);
return [...arrayVals, ...arrayArrs.flatMap((arr) => getLastIds(arr))];
});
console.log(getLastIds(section.fileds));
// output is (7) ["some Id-1", "some Id-2", "kxf5", "ed5t", "some Id-3", "ccfr", "kdpt8"]
My code doing following, it printing in new array all ids.
It's working but I don't need all ids.
I need to return only last id in array and I should use recursion.
The output should be
(4) [" "kxf5", "ed5t", "ccfr", "kdpt8"]
P.S. here is my code in codesandbox
Is there a way to solve this problem with recursion? Please help to fix this.
You can do it with reduce.
function getLastIds (value) {
return value.reduce((prev, cur) => {
if (cur.id) {
return [ ...prev, cur.id ];
} else {
let key = ('child' in cur) ? 'child' : 'fileds';
return [ ...prev, ...getLastIds (cur[key]) ]
}
}, []);
}
You could check if a certain key exists and take this property for mapping id if status exists.
const
getValues = data => {
const array = Object.values(data).find(Array.isArray);
return array
? array.flatMap(getValues)
: 'status' in data ? data.id : [];
},
section = { fileds: [{ id: "some Id-1", type: "user-1" }, { child: [{ id: "some Id-2", type: "user-2" }, { fileds: [{ id: "kxf5", status: "pending" }] }, { fileds: [{ id: "ed5t", status: "done" }] }] }, { child: [{ id: "some Id-3", type: "teacher" }, { fileds: [{ id: "ccfr", status: null }] }, { fileds: [{ id: "kdpt8", status: "inProgress" }] }] }] },
result = getValues(section);
console.log(result);

Modify array to stop object from being nested

I have an example array
const array = [
{ obj: [{ fields: { title: 'titleValue1' } }, { fields: { title: 'titleValue2' } }] },
{ obj: [{ fields: { title: 'titleValue3' } }, { fields: { title: 'titleValue4' } }] },
]
I'm looking to modify the array to:
const modifiedArray = [
{ obj: [{ title: 'titleValue1' }, { title: 'titleValue2' }] },
{ obj: [{ title: 'titleValue3' }, { title: 'titleValue4' }] },
]
So when I loop over the modified array I can call 'obj.title' instead of 'obj.fields.title'
I think this can be achieved using .map. So far I have tried:
const ammendedArray = array.map(item => ({ ...item, title: item.map(e => e.fields) }))
But returning 'item.map is not a function'
Any help with this would be much appreciated.
In your code you are trying to use map for an item in the top level array. Which is like this for the first item,
{ obj: [{ fields: { title: 'titleValue1' } }, { fields: { title: 'titleValue2' } }] }
As you can see item is an object. You can not map through an object. What you can do is map through item.obj
const ammendedArray = array.map(item => ({ ...item, title: item.obj.map(e => e.fields) }))
But it will not solve your problem you will get a wrong array of objects like this,
[
{
obj: [{ fields: { title: 'titleValue1' } }, { fields: { title: 'titleValue2' } }],
title: [{ title: 'titleValue1' }, { title: 'titleValue2' }]
},
...
]
You will have to update the obj key instead. What you need to do is the following,
const array = [
{ obj: [{ fields: { title: 'titleValue1' } }, { fields: { title: 'titleValue2' } }] },
{ obj: [{ fields: { title: 'titleValue3' } }, { fields: { title: 'titleValue4' } }] },
]
const res = array.map((item) => {
return {
obj: item.obj.map(i => {
return i.fields
})
};
});
console.log(res);
I could reach to that like this :)
const array = [
{ obj: [{ fields: { title: 'titleValue1' } }, { fields: { title: 'titleValue2' } }] },
{ obj: [{ fields: { title: 'titleValue3' } }, { fields: { title: 'titleValue4' } }] },
]
// pass a function to map
const map1 = array.map((x)=>{
const filedsArray = [...x.obj]
x.obj = []
filedsArray.forEach((y)=>{
x.obj.push({title:y.fields.title})
})
return x
})
console.log(map1);

With Crossfilter how do I return an array of all the id values for a specific type

Forgive me, I'm not sure I'm approaching this problem correctly.
I have some data (many thousands of elements) with a type and an ID:
const data = [
{ type: 'foo', id: 1 },
{ type: 'foo', id: 3 },
{ type: 'foo', id: 5 },
{ type: 'baz', id: 8 },
{ type: 'baz', id: 10 },
{ type: 'bar', id: 11 },
{ type: 'bar', id: 13 },
{ type: 'bar', id: 17 },
...
];
With crossfilter, I want to filter by a type and return an array of all their ids.
For example: all the type 'bar' should return [10, 11, 13, 17]
My attempt was to group reduce. But I didn't get very far with it:
let ndx = crossfilter(data);
let d = ndx.dimension(d => d.type);
let reduceAdd = (p, v) => p.push(v);
let reduceRemove = (p, v) => p.filter(i => i !== v);
let reduceInitial = () => ([]);
And then something like:
d.group().reduce(reduceAdd, reduceRemove, reduceInitial)
You should use filter method in combination with map and destructing assignment.
const data = [ { type: 'foo', id: 1 }, { type: 'foo', id: 3 }, { type: 'foo', id: 5 }, { type: 'baz', id: 8 }, { type: 'baz', id: 10 }, { type: 'bar', id: 11 }, { type: 'bar', id: 13 }, { type: 'bar', id: 17 }, ], type = 'bar';
console.log(data.filter(elem => elem.type == type).map(({id}) => id));
What you've got looks pretty much correct with me. You just have to query your group by saving it to a variable
var grp = d.group().reduce(reduceAdd, reduceRemove, reduceInitial)
and then query it like
grp.top(Infinity)
This will return an array of objects. The key of one of the objects will be bar and the value of that object will be the array of records where type is bar.
Using a single forEach() is more efficient in this case instead of using filter() and then map() because you have complexity of O(n) where n is the number of objects but using filter() and then map() there will be complexity of O(n+m) where m is the number of filtered records on which you do map():
const data = [
{ type: 'foo', id: 1 },
{ type: 'foo', id: 3 },
{ type: 'foo', id: 5 },
{ type: 'baz', id: 8 },
{ type: 'baz', id: 10 },
{ type: 'bar', id: 11 },
{ type: 'bar', id: 13 },
{ type: 'bar', id: 17 },
];
let type = 'bar';
var res = [];
data.forEach((obj)=> {
if(obj.type===type){
res.push(obj.id);
}
});
console.log(res);
If there are 8 objects then you are iterating 8 times in filter and then lets say you get 4 records in filter you will then iterate 4 times to get the id value in the resultant array. Total of 12 iterations. So, in such cases I prefer to support the usage of forEach() in which there will be only 8 iterations to get the same set of array.
const data = [
{ type: 'foo', id: 1 },
{ type: 'foo', id: 3 },
{ type: 'foo', id: 5 },
{ type: 'baz', id: 8 },
{ type: 'baz', id: 10 },
{ type: 'bar', id: 11 },
{ type: 'bar', id: 13 },
{ type: 'bar', id: 17 },
];
let type = 'bar';
let result = data.reduce((acc, {type, id})=>{
if(!acc[type]) acc[type]=[];
acc[type].push(id);
return acc
},{})
console.log(result[type]);
You can also use Array.reduce here.
const data = [
{ type: 'foo', id: 1 },
{ type: 'foo', id: 3 },
{ type: 'foo', id: 5 },
{ type: 'baz', id: 8 },
{ type: 'baz', id: 10 },
{ type: 'bar', id: 11 },
{ type: 'bar', id: 13 },
{ type: 'bar', id: 17 }
];
const filteredArray = data.reduce((result, obj) => {
if (obj.type === "bar") {
result.push(obj.id)
}
return result;
}, []);
console.log(filteredArray)

How to delete particular nodes within a nested object tree in JavaScript

Here is where my algorithm skills ends. I can traverse through the object and find a certain object but I'm not able to delete the object in the same time.
Here is the object
const obj = {
children: [{
children: [
{
children: [
{
key: 'a1',
type: 'a1_type'
},
{
key: 'a2',
type: 'a2_type'
}
],
key: 'root',
type: 'root_type'
},
{
key: 'error',
type: 'error_type'
}
]
}]
}
The object with the key === 'error' object can be in any children array. I want to find it and delete the object that contains the key.
The output should be like that:
let output = findAndDeleteObjByKeyAndType('error', 'error_type')
output = {
children: [{
children: [
{
children: [
{
key: 'a1',
type: 'a1_type'
},
{
key: 'a2',
type: 'a2_type'
}
],
key: 'root',
type: 'root_type'
}
]
}]
}
Can someone help here?
Array methods like filter and every can come in handy here:
const object = {
children: [{
children: [{
children: [{
key: 'a1',
type: 'a1_type'
},
{
key: 'a2',
type: 'a2_type'
},
{
key: 'error',
type: 'error_type'
}
],
key: 'root',
type: 'root_type'
},
{
key: 'error',
type: 'error_type'
}
]
}]
}
function purgeAll (object, target) {
if (object.children) {
const keys = Object.keys(target)
object.children = object.children.filter(o =>
!keys.every(k => target[k] === o[k]) && purgeAll(o, target)
)
}
return object
}
let output = purgeAll(object, {
key: 'error',
type: 'error_type'
})
console.log(output)
.as-console-wrapper { min-height: 100%; }

Concat array from Object from Array

I'm currently trying to retrieve a list of metadata stored as an array, inside an object, inside an array. Here's a better explanatory example:
[
{
name: 'test',
metadata: [
{
name: 'Author',
value: 'foo'
},
{
name: 'Creator',
value: 'foo'
}
]
},
{
name: 'otherTest',
metadata: [
{
name: 'Created',
value: 'foo'
},
{
name: 'Date',
value: 'foo'
}
]
},
{
name: 'finalTest'
}
]
Now, my objective is to retrieve a list of metadata (by their name) without redundancy. I think that .map() is the key to success but I can't find how to do it in a short way, actually my code is composed 2 for and 3 if, and I feel dirty to do that.
The expected input is: ['Author', 'Creator', 'Created', 'Date']
I'm developping in Typescript, if that can help for some function.
You can use reduce() and then map() to return array of names.
var data = [{"name":"test","metadata":[{"name":"Author","value":"foo"},{"name":"Creator","value":"foo"}]},{"name":"otherTest","metadata":[{"name":"Created","value":"foo"},{"name":"Date","value":"foo"}]},{"name":"finalTest"}]
var result = [...new Set(data.reduce(function(r, o) {
if (o.metadata) r = r.concat(o.metadata.map(e => e.name))
return r
}, []))];
console.log(result)
You could use Set for unique names.
var data = [{ name: 'test', metadata: [{ name: 'Author', value: 'foo' }, { name: 'Creator', value: 'foo' }] }, { name: 'otherTest', metadata: [{ name: 'Created', value: 'foo' }, { name: 'Date', value: 'foo' }] }, { name: 'finalTest' }],
names = new Set;
data.forEach(a => (a.metadata || []).forEach(m => names.add(m.name)));
console.log([...names]);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var data = [{"name":"test","metadata":[{"name":"Author","value":"foo"},{"name":"Creator","value":"foo"}]},{"name":"otherTest","metadata":[{"name":"Created","value":"foo"},{"name":"Date","value":"foo"}]},{"name":"finalTest"}]
data
.filter(function(obj){return obj.metadata != undefined})
.map(function(obj){return obj.metadata})
.reduce(function(a,b){return a.concat(b)},[])
.map(function(obj){return obj.name})
A hand to hand Array.prototype.reduce() and Array.prototype.map() should do it as follows;
var arr = [
{
name: 'test',
metadata: [
{
name: 'Author',
value: 'foo'
},
{
name: 'Creator',
value: 'foo'
}
]
},
{
name: 'otherTest',
metadata: [
{
name: 'Created',
value: 'foo'
},
{
name: 'Date',
value: 'foo'
}
]
},
{
name: 'finalTest'
}
];
result = arr.reduce((p,c) => c.metadata ? p.concat(c.metadata.map(e => e.name))
: p, []);
console.log(result);

Categories

Resources