How to enhance a json object? - javascript

I am recieving a json object from an api as following:
[
{"id":998,
"client":{"id":"AF01072","name":"Vivek","lastName":"Joshi","phone":123},
"membership":{"type":"Semi-Annually","price":18000},
"start_date":"2019-11-19","end_date":"2020-05-16","payment":12000,"balance":0
},
{"id":1004,
"client":{"id":"AF01072","name":"Vivek","lastName":"Joshi","phone":123},
"membership":{"type":"Annually","price":30000},
"start_date":"2020-08-27","end_date":"2021-08-27","payment":30000,"balance":5000,
}
]
in angular when I loop to print the data 'client' information prints two times, because it is repeated two times.
<div *ngFor="let c of clientinfo">
{{c.client.name}}
{{c.membership.type}}
{{c.membership.price}}
{{c.start_date}}
{{c.end_date}}
.
.
.
</div>
How can I enhance this data so I get only client information once but the rest stay as they are?
thanks in advance

Whenever you need to group some data by some value you can use an object because object properties are guaranteed to never be duplicated. You have to be careful however because properties can be overwritten.
First, let's settle on a structure that works. The following is one such structure that can work:
{
[client.id] : {
client: client,
info : [
{ membership, start_date },
{ membership, start_date },
..
]
},
[client.id] : {
client: client,
info : [
{ membership, start_date },
{ membership, start_date },
..
]
},
..
}
There are other ways to arrange the data but the key is to exploit objects to implement a map/hash/associative array of key/value pairs. With this we can group the data from your array:
let group = {};
jsonData.forEach(x => {
// check if group already contain this client:
if (!group[x.client.id]) {
// auto-create array if does not exist
group[x.client.id] = {
client: x.client,
info: []
};
}
group[x.client.id].info.push({
membership: x.membership,
start_date: x.start_date
});
});
Now you have an object grouped by client id where each property is an object that has an info array that contains membership information.
If you need to loop over this data structure you can convert it back to an array by doing:
let groupArray = Object.values(group);
This will give you the following data structure:
[
{
client: client,
info : [
{ membership, start_date },
{ membership, start_date },
..
]
},
{
client: client,
info : [
{ membership, start_date },
{ membership, start_date },
..
]
},
..
]
An array of client objects with an info property containing an array of membership data. For your sample data it would be something like:
[
{
"client":{"id":"AF01072","name":"Vivek","lastName":"Joshi","phone":123},
"info": [
{
"membership":{"type":"Semi-Annually","price":18000},
"start_date":"2019-11-19",
"end_date":"2020-05-16",
"payment":12000,"balance":0
},
{
"membership":{"type":"Annually","price":30000},
"start_date":"2020-08-27",
"end_date":"2021-08-27",
"payment":30000,"balance":5000,
}
]
}
]

You need to create array of memberships for each client like below. And in the template file, you need to use two *ngFor loops. One to loop client details and another one to loop the respective client's membership details.
Working stackblitz
const result = [{
"id": 998,
"client": {
"id": "AF01072",
"name": "Vivek",
"lastName": "Joshi",
"phone": 123
},
"membership": {
"type": "Semi-Annually",
"price": 18000
},
"start_date": "2019-11-19",
"end_date": "2020-05-16",
"payment": 12000,
"balance": 0
},
{
"id": 1004,
"client": {
"id": "AF01072",
"name": "Vivek",
"lastName": "Joshi",
"phone": 123
},
"membership": {
"type": "Annually",
"price": 30000
},
"start_date": "2020-08-27",
"end_date": "2021-08-27",
"payment": 30000,
"balance": 5000,
},
{
"id": 1005,
"client": {
"id": "AF01073",
"name": "Some Other",
"lastName": "Joshi",
"phone": 123
},
"membership": {
"type": "Annually",
"price": 30000
},
"start_date": "2020-08-27",
"end_date": "2021-08-27",
"payment": 30000,
"balance": 5000,
}
].reduce((acc, item, i) => {
if (i == 0) {
acc.push(constructCorrectObject(item));
return acc;
}
let foundItem = acc.find(it => it.id === item.client.id);
if (foundItem) {
addMemeberShipDetails(foundItem, item);
return acc;
} else {
acc.push(constructCorrectObject(item));
return acc;
}
}, []);
function constructCorrectObject(item) {
return {
"id": item.client.id,
"name": item.client.name,
"lastName": item.client.lastName,
"phone": item.client.phone,
"membership": [{
"id": item.id,
"type": item.membership.type,
"price": item.membership.price,
"start_date": item.start_date,
"end_date": item.end_date,
"payment": item.payment,
"balance": item.balance,
}]
}
}
function addMemeberShipDetails(acc, item) {
acc.membership.push({
"id": item.id,
"type": item.membership.type,
"price": item.membership.price,
"start_date": item.start_date,
"end_date": item.end_date,
"payment": item.payment,
"balance": item.balance,
});
}
console.log(result);

Related

update nested array data in mongodb and node js

I want to update status in corder data. i try many time but i cannot update that data. if you know how update that data ??
{
"_id": "63064232cf92b07e37090e0a",
"sname": "Bombay collection ",
"name": "Hussain Khan",
"costomer": [
{
"cname": "Basan",
"cphone": 9863521480,
"_id": "632eeff2a9b88eb59d0210f0",
"corder": [
{
"clothType": "Shirt",
"date": "2022-10-21",
"status": "false",
"_id": "635283363edde6a0e9e92dc0"
},
]
}
]
}
You can use $[<identifier>]. For example:
db.collection.update(
{},
{$set: {"costomer.$[elemA].corder.$[elemB].status": newStatus}},
{arrayFilters: [
{"elemA._id": costomerId},
{"elemB._id": corderId}
]}
)
See how it works on the playground example

How to get actual keys from MongoDB result array?

INPUT:
let array =[
{name:"xyz", email:"xyz#gmail.com", date:"22/10/2020" },
{name:"abc", email:"abc#gmail.com", date:"29/12/2020" },
{name:"efg", email:"efg#gmail.com", date:"20/01/2021" },
{name:"pqr", email:"pqr#gmail.com", date:"12/08/2020", age:"20" },
{name:"stu", email:"stu#gmail.com", date:"19/09/2020", age:"21" },
]
After performing certain operations I got the above array of objects as output from mongodb collection. From that array I want to get all keys and compare each and every object in an array and if any property present in any object not there in another object then I want to assign the property with empty string value.
To perform that operation I have written the code as shown below-
const keys = array.reduce(
(acc, curr) => (Object.keys(curr).forEach((key) => acc.add(key)), acc),
new Set()
);
const output = array.map((item) =>
[...keys].reduce((acc, key) => ((acc[key] = item[key] ?? ""), acc), {})
);
By performing above operations along with my original keys I am getting unnecessary keys like "$__", "$isNew", "_doc"
MY OUTPUT:
[
{
"$__": {
"activePaths": {
"paths": {
"_id": "init"
},
"states": {
"ignore": {},
"default": {},
"init": {
"_id": true
},
"modify": {},
"require": {}
},
"stateNames": [
"require",
"modify",
"init",
"default",
"ignore"
]
},
"strictMode": false,
"skipId": true,
"selected": {},
"fields": {},
"exclude": null
},
"$isNew": false,
"_doc": {
"_id": "62e6791b049e103f612f0882",
"name": "pqr",
"email": "pqr#gmail.com",
"date": "12/8/2020",
"age": "20",
},
"name": "pqr",
"email": "pqr#gmail.com",
"date": "12/8/2020",
"age": "20",
},
.
.
.
.
EXPECTED OUTPUT:
[
{
"name": "xyz",
"email": "xyz#gmail.com",
"date": "22/10/2020",
"age": ""
},
{
"name": "abc",
"email": "abc#gmail.com",
"date": "29/12/2020",
"age": ""
},
{
"name": "efg",
"email": "efg#gmail.com",
"date": "20/01/2021",
"age": ""
},
{
"name": "pqr",
"email": "pqr#gmail.com",
"date": "12/08/2020",
"age": "20"
},
{
"name": "stu",
"email": "stu#gmail.com",
"date": "19/09/2020",
"age": "21"
}
]
If I am using same code for normal array instead of resultant array from mongoodb I am getting correct output.
Please help me in resolving the above issue.
Query
if you have few fields and you know their names you can do it on server like bellow, in simple way
check if field (if missing it will be false) if its there keep old value, else add it with ""
the bellow doesn't work with boolean fields, if you have those you should check if type="missing" but in your case you dont have boolean fields so it will be ok
Playmongo
aggregate(
[{"$set":
{"name": {"$cond": ["$name", "$name", ""]},
"email": {"$cond": ["$email", "$email", ""]},
"date": {"$cond": ["$date", "$date", ""]},
"age": {"$cond": ["$age", "$age", ""]}}}])

Replacing Data in Array

I am working on an angular application. I have an array as follows:
[{
"Name": "Andy"
},
{
"Name": "Bayer"
},
{
"Name": "James"
},
{
"Name": "Doda"
}]
I have another array which containes data as follows:
[
{
"Name": "Andy",
"Id": "1",
"Time": "2020-06-19T11:02+00:00"
},
{
"Name": "Billy",
"Id": "2",
"Time": "2020-06-19T11:05+00:00"
},
{
"Name": "Ciena",
"Id": 5
"Time": "2020-06-19T11:05+00:00"
},
{
"Name": "Doda",
"Id": "4",
"Time": "2020-06-19T11:05+00:00"
}
]
I want a resultant array such that code should check if Name is present in first array, then it should copy data from second array for that Name and push it in resultant array. For example common name between above two array is Andy and Doda, so data from Andy and Doda should be pushed to resultant array as follows:
[{
"Name": "Andy",
"Id": "1",
"Time": "2020-06-19T11:02+00:00"
},
{
"Name": "Bayer"
},
{
"Name": "James"
},
{
"Name": "Doda",
"Id": "4",
"Time": "2020-06-19T11:05+00:00"
}]
At run time I may get many names so code should be generic. I was trying following code which I got over stackoverflow itself
this.newArray = _.map(this.resultantArray, item => {
const value = _.find(this.dataArray, ['Name', item]);
const obj = value ? value : {Name: item};
return obj;
});
But this code is not working as expected as it works fine for the first time but when data comes for second time it appends data to previous data. I want array to be populated again freshly every time I send data. Please help
You can do this with vanilla JS no need for lodash. You can first map it and inside that you can find the value from second array otherwise return the current object:
var arrayTwo = [ { "Name": "Andy", "Id": "1", "Time": "2020-06-19T11:02+00:00" }, { "Name": "Billy", "Id": "2", "Time": "2020-06-19T11:05+00:00" }, { "Name": "Ciena", "Id": "5", "Time": "2020-06-19T11:05+00:00" }, { "Name": "Doda", "Id": "4", "Time": "2020-06-19T11:05+00:00" } ];
var arrayOne = [{ "Name": "Andy"}, { "Name": "Bayer"}, { "Name": "James"}, { "Name": "Doda"}];
var result = arrayOne.map(val=>arrayTwo.find(p=>p.Name==val.Name) || val);
console.log(result);
Suppose first array name is First
First : any [] = [{"Name": "Andy"},{"Name": "Bayer"},{ "Name": "James"},{"Name": "Doda"}]
And Second array name is Second
Second : any[] = [{"Name": "Andy","Id": "1","Time": "2020-06-19T11:02+00:00"},{"Name": "Bayer"},{"Name": "James"},{"Name": "Doda","Id": "4","Time": "2020-06-19T11:05+00:00"}]
Now do looping and check each name of first if its exists in second copy from second and push in result array
result : any[] =[];
this.First.forEach((element) => {
let index = this.Second.findIndex((x) => element.Name== x.Name);
if (index > -1) {
let data = {
this.Second[index].Name,
this.Second[index].Id,
this.Second[index].time,
};
this.result.push(data);
}
}

Value update if object exist and create if not under array mongodb

I have a dataset like this:
[
{
"createdate": "2020-06-03T18:30:00.000Z",
"hold": [
{
"time": "12:30",
"user": [
"5ed8ae4891501a1c10246dfe"
]
},
{
"time": "13:00",
"user": [
"5ed8ae4891501a1c10246dfe"
]
},
{
"time": "13:30",
"user": [
"5ed8ae4891501a1c10246dfe"
]
}
],
"_id": "5ed745c38b4d8646601bad1d",
"__v": 0
},
{
"createdate": "2020-06-04T18:30:00.000Z",
"hold": [
{
"time": "12:30",
"user": [
"5ed8ae4891501a1c10246dfe"
]
},
{
"time": "13:00",
"user": [
"5ed8ae4891501a1c10246dfe"
]
},
{
"time": "13:30",
"user": [
"5ed8ae4891501a1c10246dfe"
]
}
],
"_id": "5ed745c3404a701fb888e732",
"__v": 0
}
]
Now I want to update some data on these two enteries, let's say I have to update "13:00" on both the enteries to push 5ed8ae4891501a1c10246dfe, and create/push new object "14:30" with user id i.e 5ed8ae4891501a1c10246dfe.
Also check if id already exist on user array then don't perform anything.
userdata = [{"startdate": "2020, 6, 4", "enddate": "2020, 6, 4", "time": ["12:30", "13:00","13:30"]}]
userdata is the data which we have to update on collection.
What I tried is like:
let userdata = [{ "startdate": "2020, 6, 4", "enddate": "2020, 6, 5", "time": ["12:30", "13:00", "13:30"] }]
const test = async () => {
try {
// I am Using mongoose, so CollectionName is dummy name to share
let data = [];
let getCalenderData = await CollectionName.find(
{ createdate: { $gte: new Date(userdata[0].startdate), $lte: new Date(userdata[0].enddate) } }
).exec();
console.log(getCalenderData)
if (getCalenderData.length > 0) {
data = getCalenderData;
}
console.log(JSON.stringify(data, null, 3))
// So here what I tried to achieve is first on one query pull all data,
// then using filter function to segregate the data,
// then after this set push or pull the data at single query.
} catch (error) {
console.log(error)
}
}
test();
But I have confusion that this approach would be little lengthier than Database query. We have tan option to solve this through javascript but in terms of Database there must me some smart query which perform all these task in a few lines rather than using the Javascript.
Any expert of MongoDB Database, please suggest me how to perform this through DB Query rather than javascript. Any help from the experts will be really really appreciated. Thanks in Advance

Lodash to filter out an array of objects

I am trying to filter out a nested array of objects using lodash which is pretty straightforward but I am looking to avoid multiple calls.
I am looking to create 2 array of objects using a single lodash call/function. Looking to find object property "$isMultiAccount" if it exists put the whole object into one result set and if not put it to another ruleset.
Currently I am doing this with Lodash "has and filter" for first and for other "!has" which means same object is looped twice , as object is relatively large its creating bottleneck for speed
https://repl.it/repls/HomelyExpensiveTruetype
const item = {
"domains": [
{
"id": "dm11022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"$isMultiAccount": "./Yes"
}
]
}
}
},
{
"id": "dm12022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"$isMultiAccount": "./No"
}
]
}
}
},
{
"id": "dm12022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"conf": {
"isVpnBased":{
"accountType": "Primary"
}
}
}
]
}
}
}
]
}
/*
Expected result
output1 = [
{
"id": "dm11022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"$isMultiAccount": "./Yes"
}
]
}
}
},
{
"id": "dm12022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"$isMultiAccount": "./No"
}
]
}
}
}
]
// $isMultiAccount account do not exist in this object
output2 = [
{
"id": "dm12022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"conf": {
"isVpnBased":{
"accountType": "Primary"
}
}
}
]
}
}
}
]
*/
const item = {
"domains": [
{
"id": "dm11022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"$isMultiAccount": "./Yes"
}
]
}
}
},
{
"id": "dm12022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"$isMultiAccount": "./No"
}
]
}
}
},
{
"id": "dm12022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"conf": {
"isVpnBased":{
"accountType": "Primary"
}
}
}
]
}
}
}
]
}
const [areMulti, areNotMulti] = _.reduce(item.domains, (current, next) => {
return _.has(next, ‘information.owner.others.$isMultiAccount’)
? [current[0].concat(next), current[1]]
: [current[0], current[1].concat(next)];
}, [[], []]);
console.log(areMulti);
console.log(areNotMulti);
Since item.domains.information.owner.others is an array, you need to tackle it as follows:
let multi = [];
let notMulti = [];
_.each(item.domains, function (obj) {
if (obj.information.owner.others.length && _.has(obj.information.owner.others[0], '$isMultiAccount'))
multi.push(obj);
else
notMulti.push(obj);
});
console.log(multi);
console.log(notMulti);
Unfortunately, you have to iterate over the domains array as well ass on the owner.others array to determine if the object with specific key sits inside.
So the algorithm has O(n*m) complexity.
If you ask for a lodash function seems that the partition method is what you're looking for
As docs says:
Creates an array of elements split into two groups, the first of which contains elements predicate returns truthy for, the second of which contains elements predicate returns falsey for. The predicate is invoked with one argument: (value).
So it will be like:
_.partition(
item.domains,
e => _.some(
_.get(e, 'information.owner.others'),
el => _.has(el,"$isMultiAccount")
)
);
Watch out - some hack available!
However, if the you're 100% sure that the element you're looking for will be always at specific index (for example it is supposed to be always as first element - so index 0) you can limit the algorithm to have linear complexity O(n) as only the size of the domains array will matter in terms of performance.
The hackish solution assuming fixed array index=0:
_.partition(
item.domains,
e => _.has(e, 'information.owner.others.0.$isMultiAccount')
);
NOTE
Using lodash makes code a bit easier to read but of course it creates some performance overhead anyway.

Categories

Resources