Move child JSON objects to their own object with parent ID - javascript

I am pretty new to JavaScript, and am working with Node.JS to access an API and store the response in a SQL Server. I am using the "request" and "mssql" Node packages. I am not attached to these, they just seemed to be what I needed and have good documentation and support.
My question: I have the JSON response from the API in the following format:
requests = [ {
url: 'https://domain.zendesk.com/api/v2/requests/2.json',
id: 2,
status: 'closed',
priority: 'normal',
type: 'incident',
subject: 'Test Ticket',
description: 'Test ticket',
organization_id: 10101010101,
via: {
channel: 'email',
source: {
from: {
address: 'bill.bob#domain.com',
name: 'Bill Bob'
},
to: {
name: 'Company Helpdesk',
address: 'testzendesk#domain.com'
},
rel: null
},
},
custom_fields:[
{ id: 31368658, value: null },
{ id: 29221487, value: null },
{ id: 31636418, value: null },
{ id: 29498078, value: null },
{ id: 31659217, value: null }
],
requester_id: 2020202020,
collaborator_ids: [],
is_public: true,
due_at: null,
can_be_solved_by_me: false,
created_at: '2015-03-05T05:55:22Z',
updated_at: '2015-03-12T05:01:51Z',
recipient: 'testzendesk#domain.com',
followup_source_id: null,
assignee_id: 30303030303,
ticket_form_id: null,
fields: [
{ id: 31368658, value: null },
{ id: 29221487, value: null },
{ id: 31636418, value: null },
{ id: 29498078, value: null },
{ id: 31659217, value: null }
]
},
{
url: 'https://domain.zendesk.com/api/v2/requests/2.json',
id: 3,
status: 'closed',
priority: 'normal',
type: 'incident',
subject: 'Test Ticket',
description: 'Test ticket',
organization_id: 10101010101,
via: {
channel: 'email',
source: {
from: {
address: 'bill.bob#domain.com',
name: 'Bill Bob'
},
to: {
name: 'Company Helpdesk',
address: 'testzendesk#domain.com'
},
rel: null
}
},
custom_fields: [
{ id: 31368658, value: null },
{ id: 29221487, value: null },
{ id: 31636418, value: null },
{ id: 29498078, value: null },
{ id: 31659217, value: null }
],
requester_id: 2020202020,
collaborator_ids: [],
is_public: true,
due_at: null,
can_be_solved_by_me: false,
created_at: '2015-03-05T05:55:22Z',
updated_at: '2015-03-12T05:01:51Z',
recipient: 'testzendesk#domain.com',
followup_source_id: null,
assignee_id: 30303030303,
ticket_form_id: null,
fields: [
{ id: 31368658, value: null },
{ id: 29221487, value: null },
{ id: 31636418, value: null },
{ id: 29498078, value: null },
{ id: 31659217, value: null }
]
} ];
I need to pull the children objects out, i.e. the "via", "custom_fields" and "fields" with the parent IDs. So for the first object, each of the "via" children objects would also have the ID of 2, and would have "channel", "source", and "ID" elements.
Something like this:
parents:
[
{
url: 'https://domain.zendesk.com/api/v2/requests/2.json',
id: 2,
status: 'closed',
priority: 'normal',
type: 'incident',
subject: 'Test Ticket',
description: 'Test ticket',
organization_id: 10101010101,
requester_id: 2020202020,
collaborator_ids: [],
is_public: true,
due_at: null,
can_be_solved_by_me: false,
created_at: '2015-03-05T05:55:22Z',
updated_at: '2015-03-12T05:01:51Z',
recipient: 'testzendesk#domain.com',
followup_source_id: null,
assignee_id: 30303030303,
ticket_form_id: null
},
{ url: 'https://domain.zendesk.com/api/v2/requests/2.json',
id: 3,
status: 'closed',
priority: 'normal',
type: 'incident',
subject: 'Test Ticket',
description: 'Test ticket',
organization_id: 10101010101,
requester_id: 2020202020,
collaborator_ids: [],
is_public: true,
due_at: null,
can_be_solved_by_me: false,
created_at: '2015-03-05T05:55:22Z',
updated_at: '2015-03-12T05:01:51Z',
recipient: 'testzendesk#domain.com',
followup_source_id: null,
assignee_id: 30303030303,
ticket_form_id: null
}
]
via:
[
{
channel: 'email',
parent_id: 2
},
{
channel: 'email',
parent_id: 3
}
]
via_source_from:
[
{
address: 'bill.bob#domain.com',
name: 'Bill Bob',
parent_id: 2
},
{
address: 'bill.bob#domain.com',
name: 'Bill Bob',
parent_id: 2
}
]
via_source_to:
[
{
name: 'Company Helpdesk',
address: 'testzendesk#domain.com',
parent_id: 2
},
{
name: 'Company Helpdesk',
address: 'testzendesk#domain.com',
parent_id: 2
}
]
custom_fields:
[
{ parent_id: 2, id: 31368658, value: null },
{ parent_id: 2, id: 29221487, value: null },
{ parent_id: 2, id: 31636418, value: null },
{ parent_id: 2, id: 29498078, value: null },
{ parent_id: 2, id: 31659217, value: null },
{ parent_id: 3, id: 31368658, value: null },
{ parent_id: 3, id: 29221487, value: null },
{ parent_id: 3, id: 31636418, value: null },
{ parent_id: 3, id: 29498078, value: null },
{ parent_id: 3, id: 31659217, value: null }
]
fields:
[
{ parent_id: 2, id: 31368658, value: null },
{ parent_id: 2, id: 29221487, value: null },
{ parent_id: 2, id: 31636418, value: null },
{ parent_id: 2, id: 29498078, value: null },
{ parent_id: 2, id: 31659217, value: null },
{ parent_id: 3, id: 31368658, value: null },
{ parent_id: 3, id: 29221487, value: null },
{ parent_id: 3, id: 31636418, value: null },
{ parent_id: 3, id: 29498078, value: null },
{ parent_id: 3, id: 31659217, value: null }
]
I have searched around, and have not found anything that would allow me to do this.
Thanks a bunch!

You can do something like this, there isn't much to describe but still if couldn't understand anything in below code, please comment.
I haven't manipulated the parents object, but you can delete the desired fields yourself. you can refer to this link. But remember to manipulate after cloning the object because delete operator mutate the original object.
let
parents = [],
via = [],
via_source_from = [],
via_source_to = [],
custom_fields = [],
fields = [];
requests.forEach( record => {
let pid = record.id;
parents = [ ...parents, record ];
via = [ ...via, Object.assign({}, { parent_id: pid, channel: record.via.channel } ) ];
via_source_from = [ ...via_source_from, Object.assign({}, { parent_id: pid }, record.via.source.from ) ]
via_source_to = [ ...via_source_to, Object.assign({}, { parent_id: pid }, record.via.source.to ) ]
custom_fields = [ ...custom_fields, ...record.custom_fields.map( f => { return Object.assign({}, f, { parent_id: pid }) } ) ]
fields = [ ...fields, ...record.fields.map( f => { return Object.assign({}, f, { parent_id: pid }) } ) ]
});
console.log("parent: ", parent);
console.log("via: ", via);
console.log("via_source_from: ", via_source_from);
console.log("via_source_to: ", via_source_to);
console.log("custom_fields: ", custom_fields);
console.log("fields: ", fields);
Update
I have just created an empty array in start to add the specific data on each iteration of requests. Then concatenate the array with relevant data using mainly four concepts, given below.
Spread Operator
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
var arr3 = [...arr1, ...arr2]; // returns new array [0, 1, 2, 3, 4, 5]
var arr4 = [...arr3, 6]; // returns new array [0, 1, 2, 3, 4, 5, 6]
This is new spread operator of javascript. You can refer this link for better understanding. In case of array, it works same as array.concat just more clean syntax.
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.
var obj = { a: 1 };
var copy = Object.assign({}, obj, { b: 2 }); // we can add as many object as we need.
copy.c = 3;
console.log(obj); // { a: 1 }
console.log(copy); // { a: 1, b: 2, c: 3 }
A great way to clone the object without having a reference to the original object. In short, to create immutable object. You can refer this link for better understanding.
Array.prototype.map()
The map() method creates a new array with the results of calling a provided function on every element in the calling array.
var numbers = [1, 5, 10, 15];
var doubles = numbers.map(function(x) {
return x * 2;
});
// doubles is now [2, 10, 20, 30]
// numbers is still [1, 5, 10, 15]
Remember, it returns a new array and doesn't affect the original array. You have to return something from internal function else, you will have undefined at that index in final array. You can refer this link for better understanding.
Arrow Function
An arrow function expression has a shorter syntax than a function expression and does not bind its own this, arguments, super, or new.target.
[ 'hi', 'ola', 'hello' ].map( greet => greet.length );
// returns [ 2, 3, 5 ]
Just a shorter syntax for writing a function. One main point is it doesn't bind its own this unlike function keyword, it really helps in defining scope of the this. You can refer this link for better understanding.

Related

Filter Array of objects with nested array

so I am trying to set up a nested filter on an array of objects.
The thing is that the filter is applied inside the object on a key that is another array of objects.
here is the code:
const items = [
{ name: "123", id: 1, value: true, arr: [{ id: 1 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 2 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 3 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 4 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 5 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 6 }] },
];
const newArray = items.filter((objects) => {
objects.arr.filter((item) => {
if (item.id === 2) {
return objects;
}
});
});
console.log(newArray);
I 'm not sure where to put the return because in this situation i just get an empty array.
You need to check the nested array contains the wanted id and return the result to the filter method.
const
items = [{ name: "123", id: 1, value: true, arr: [{ id: 1 }] }, { name: "456", id: 2, value: false, arr: [{ id: 2 }] }, { name: "456", id: 2, value: false, arr: [{ id: 3 }] }, { name: "456", id: 2, value: false, arr: [{ id: 4 }] }, { name: "456", id: 2, value: false, arr: [{ id: 5 }] }, { name: "456", id: 2, value: false, arr: [{ id: 6 }] }],
result = items.filter(({ arr }) => arr.some(({ id }) => id === 2));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Use Array#some to check if current arr has an element with id equal to 2:
const items = [ { name: "123", id: 1, value: true, arr: [{ id: 1 }] }, { name: "456", id: 2, value: false, arr: [{ id: 2 }] }, { name: "456", id: 2, value: false, arr: [{ id: 3 }] }, { name: "456", id: 2, value: false, arr: [{ id: 4 }] }, { name: "456", id: 2, value: false, arr: [{ id: 5 }] }, { name: "456", id: 2, value: false, arr: [{ id: 6 }] } ];
const newArray = items.filter(({ arr = [] }) =>
arr.some(({ id }) => id === 2)
);
console.log(newArray);
As you contains only one object in arr then you can access this object by using [0] index.
Working Demo :
const items = [
{ name: "123", id: 1, value: true, arr: [{ id: 1 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 2 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 3 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 4 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 5 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 6 }] },
];
const newArray = items.filter((obj) => {
if (obj.arr[0].id === 2) {
return obj;
}
});
console.log(newArray);
I answered you as per the issue you are facing but if you have multiple objects in your arr then you can go ahead with Array.some() method as suggested by other answers.

Assign new properties to an array of Objects

I have an array of Objects
const options = [
{ id: 1, name: "Back Pain" },
{ id: 2, name: "Body aches" },
{ id: 3, name: "Cold Sores" },
{ id: 4, name: "Cough" },
{ id: 5, name: "Constipation" },
];
I am trying to write a function that will assign new properties to the object.
The output I am looking for is:
const options = [
{ value: 1, label: "Back Pain" },
{ value: 2, label: "Body aches" },
{ value: 3, label: "Cold Sores" },
{ value: 4, label: "Cough" },
{ value: 5, label: "Constipation" },
];
I have tried to loop through the array using a for loop, but can not figure it out.
Thanks for the help:)
You can do it like this:
const data=[{ id: 1, name: "Back Pain" },
{ id: 2, name: "Body aches" },
{ id: 3, name: "Cold Sores" },
{ id: 4, name: "Cough" },
{ id: 5, name: "Constipation" },
];
var result = data.map(({id:value, name:label})=>({value, label}));
console.log(result);

Which is the best way to get an array difference from an object?

I have an array of values: ["1", "2", "3"] which contains essentially the reference of the records stored in this array of object:
[
{ id: 1, name: "John" },
{ id: 2, name: "Patrick" },
{ id: 3, name: "Jack" },
{ id: 4, name: "Paula" },
{ id: 5, name: "Sarah" }
]
I would like to return the missing reference from the array of objects, so the result will be: 4, 5. What I achieved so far is takes all the selected values of the first array from all the select available in the html:
var selected_options = $('.options-picker')
.map(function() { return this.value}).get();
this will return 1, 2, 3. How can I extract from the array of objects 4, 5?
Thanks in advance.
Use filter and includes to check the object ids against the values in the array.
const data = [
{ id: 1, name: "John" },
{ id: 2, name: "Patrick" },
{ id: 3, name: "Jack" },
{ id: 4, name: "Paula" },
{ id: 5, name: "Sarah" }
];
const items = [1, 2, 3];
const out = data.filter(obj => !items.includes(obj.id));
console.log(out);
This will do
var a=[
{ id: 1, name: "John" },
{ id: 2, name: "Patrick" },
{ id: 3, name: "Jack" },
{ id: 4, name: "Paula" },
{ id: 5, name: "Sarah" }
]
var b=['1', '2', '3'];
a.forEach((e)=>{
if(b.indexOf(e.id.toString())==-1)
{
b.push(e.id);
}
})
alert(b)

filter inside forEach does no work properly when finds double objects

I try to get each object of an array and compare it to the objects of another array. If they match, remove the object from the second array.
Strange thing is that if an object is found two times in an array, that object is not filtered.
I want to compare newdata to existing. If an object of newdata has the same id and cat, it will not be in the new array.
existing is
var existing = [{
name: "John",
values_: {
id: 5,
cat: true
}
},
{name: "Jake",
values_: {
id: 3,
cat: true
}
},
{
name: "Alice",
values_: {
id: 2,
cat: false
}
}
];
newdata is
var newdata = [{
name: "Mike",
properties: {
id: 1,
cat: true
}
},
{name: "Jake",
properties: {
id: 3,
cat: true
}
},
{name: "Jake",
properties: {
id: 3,
cat: true
}
},
{
name: "Alice",
properties: {
id: 2,
cat: false
}
}
];
and my filter is
existing.forEach((existingitem, existingindex, existingfeatures) => {
newdata2 = newdata.filter(item => (
existingitem.values_.id != item.properties.id &&
existingitem.values_.cat != item.properties.cat
));
});
console.log('newdata2 - ',newdata2);
The logic thing is for newdata2 to have only Mike . The problem is that I can see Jake two times. Jake should not be there, its already in existing.
If I edit newdata like so (no doubles)
var newdata = [{
name: "Mike",
properties: {
id: 1,
cat: true
}
},
{name: "Jake",
properties: {
id: 3,
cat: true
}
} ,
{
name: "Alice",
properties: {
id: 2,
cat: false
}
}
];
I still can see Jake in newdata2. But why?
Please help me fix this. Is it my filter or the way filter works? Based on the criteria, I should only get Mike at the end. Please advice.
Thank you
var existing = [{
name: "John",
values_: {
id: 5,
cat: true
}
},
{name: "Jake",
values_: {
id: 3,
cat: true
}
},
{
name: "Alice",
values_: {
id: 2,
cat: false
}
}
];
var newdata = [{
name: "Mike",
properties: {
id: 1,
cat: true
}
},
{name: "Jake",
properties: {
id: 3,
cat: true
}
},
{name: "Jake",
properties: {
id: 3,
cat: true
}
},
{
name: "Alice",
properties: {
id: 2,
cat: false
}
}
];
existing.forEach((existingitem, existingindex, existingfeatures) => {
newdata2 = newdata.filter(item => (
existingitem.values_.id != item.properties.id &&
existingitem.values_.cat != item.properties.cat
));
});
console.log('newdata2 - ',newdata2);
... think about a filter (outer) and every (inner) based approach instead of forEach and filter - maybe that makes it easier to think about a correct implementation.
var existingItemList = [{ name: "John", values_: { id: 5, cat: true }}, { name: "Jake", values_: { id: 3, cat: true }}, { name: "Alice", values_: { id: 2, cat: false }}];
var newItemList = [{ name: "Mike", properties: { id: 1, cat: true }}, { name: "Jake", properties: { id: 3, cat: true }}, { name: "Jake", properties: { id: 3, cat: true }}, { name: "Alice", properties: { id: 2, cat: false }}];
var itemList = newItemList.filter(function (newItem) { // filter `newItem` only
return existingItemList.every(function (existingItem) { // if it does not exist
return ( // in `existingItemList`.
//(newItem.name !== existingItem.name) &&
(newItem.properties.id !== existingItem.values_.id)
);
});
});
console.log('itemList : ', itemList);
.as-console-wrapper { max-height: 100%!important; top: 0; }
EDIT
follow up, referring this comment of mine ...
your comparison condition just does not fit what you are really searching/looking for.
If one still assumes that the OP wants to filter a new item only if it does not already exist in another list that it is going to be compared to ...
... one has to write a matcher function that maps and compares item fields in one and the same time.
This comparator/matcher then has to be used in a way that it will filter only the very new item that does not equal any other already existing item.
This can be achieved by a slight change to the former approach from above ...
function doesExistingItemMatchBoundNewItem(existingItem) {
var newItem = this;
return (
(newItem.properties.id === existingItem.values_.id)
&& (newItem.properties.cat === existingItem.values_.cat)
);
}
var existingItemList = [{ name: "John", values_: { id: 5, cat: true }}, { name: "Jake", values_: { id: 3, cat: true }}, { name: "Alice", values_: { id: 2, cat: false }}];
var newItemList = [{ name: "Mike", properties: { id: 1, cat: true }}, { name: "Jake", properties: { id: 3, cat: true }}, { name: "Jake", properties: { id: 3, cat: true }}, { name: "Alice", properties: { id: 2, cat: false }}];
var itemList = newItemList.filter(function (newItem) {
return !existingItemList.some(doesExistingItemMatchBoundNewItem.bind(newItem));
});
console.log('itemList : ', itemList);
.as-console-wrapper { max-height: 100%!important; top: 0; }
You could take a Set and filter the known items.
var existing = [{ name: "John", values_: { id: 5, cat: true } }, { name: "Jake", values_: { id: 3, cat: true } }, { name: "Alice", values_: { id: 2, cat: false } }],
newdata = [{ name: "Mike", properties: { id: 1, cat: true } }, { name: "Jake", properties: { id: 3, cat: true } }, { name: "Jake", properties: { id: 3, cat: true } }, { name: "Alice", properties: { id: 2, cat: false } }],
eSet = new Set(existing.map(({ values_: { id, cat } }) => [id, cat].join('|'))),
result = newdata.filter(({ properties: { id, cat } }) => !eSet.has([id, cat].join('|')));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to properly un-nest and flatten an array of javascript objects with an array inside them (X-amount of times)

I'm currently working with an array of javascript objects with X-amount of array's with the same type of object inside it, in a specific property (in this case, 'modelChildren')
I would like to flatten this into just one array of objects, and return the level at which it was found. The solution does not have to be plain javascript, as I use lodash for many situations. Ideally I would also like to remove the 'modelChildren' field once finished.
Any help would be appreciated.
Thanks!
input:
[{
id: 1,
name: foo
modelChildren: [
{
id: 2,
name: bar,
modelChildren: [
{
id: 3,
name: foobar
},
{
id: 4,
name: foobarfoo
}
]
}
]
}]
expected result:
[{
id: 1,
name: foo,
level: 1
{
id: 2,
name: bar,
level: 2
},
{
id: 3,
name: foobar,
level: 3
},
{
id: 4,
name: foobarfoo
level: 3
}]
This can be quite easy, it is just Tree Traversal
So you just need to traverse it and remember the level, while storing "nodes" when you are in them.
For example this code
const source = [{
id: 1,
name: 'foo',
modelChildren: [
{
id: 2,
name: 'bar',
modelChildren: [
{
id: 3,
name: 'foobar'
},
{
id: 4,
name: 'foobarfoo'
}
]
}
],
}, {
id: 5,
name: 'foo',
modelChildren: [
{
id: 6,
name: 'bar',
modelChildren: [
{
id: 7,
name: 'foobar'
},
{
id: 8,
name: 'foobarfoo'
}
]
},
{
id: 9,
name: 'bar',
modelChildren: [
{
id: 10,
name: 'foobar'
},
{
id: 11,
name: 'foobarfoo'
}
]
}
],
}
];
const newSource = [];
const _ = require('lodash');
function doIt(items, level) {
if (!items) {
return;
}
items.forEach(item => {
newSource.push(_.merge({level}, _.pick(item, ['id', 'name'])));
doIt(item.modelChildren, level + 1);
})
}
doIt(source, 1);
console.log(newSource);
Having this output
[ { level: 1, id: 1, name: 'foo' },
{ level: 2, id: 2, name: 'bar' },
{ level: 3, id: 3, name: 'foobar' },
{ level: 3, id: 4, name: 'foobarfoo' },
{ level: 1, id: 5, name: 'foo' },
{ level: 2, id: 6, name: 'bar' },
{ level: 3, id: 7, name: 'foobar' },
{ level: 3, id: 8, name: 'foobarfoo' },
{ level: 2, id: 9, name: 'bar' },
{ level: 3, id: 10, name: 'foobar' },
{ level: 3, id: 11, name: 'foobarfoo' } ]

Categories