Applying multiple groupings using lodash - arrays

I would like to group a JSON array by two properties (name and address2) and then reduce it. Here is the current JSON:
{
"clientId": "FOO",
"name": "John Doe",
"address1": "London",
"address2": "1 High Street",
"invoiceNumber": "1234",
"itemDescription": "Item 1",
"itemQuantity": 1
},
{
"clientId": "BAR",
"name": "John Doe",
"address1": "London",
"address2": "1 Oxford Street",
"invoiceNumber": "0987",
"itemDescription": "Item 2",
"itemQuantity": 1
},
{
"clientId": "FOO",
"name": "John Doe",
"address1": "London",
"address2": "1 High Street",
"invoiceNumber": "5678",
"itemDescription": "Item 3",
"itemQuantity": 1
}
This is what I would like to achieve:
[
{
"clientId": "FOO",
"name": "John Doe",
"address1": "London",
"address2": "1 High Street",
"deliveries": [
{
"invoiceNumber": "1234",
"itemQuantity": 1,
"itemDescription": "Item 1"
},
{
"invoiceNumber": "5678",
"itemDescription": "Item3",
"itemQuantity": 1
}
]
},
{
"clientId": "BAR",
"name": "JohnDoe",
"address1": "London",
"address2": "12 Oxford Street",
"deliveries": [
{
"invoiceNumber": "0987",
"itemDescription": "Item2",
"itemQuantity": 1
}
]
}
]
And here is the code that I have so far, which doesn't produce what I would hope it would:
var groups = _.groupBy(rows, function(value) {
return value.name + '#' + value.address2;
});
var data = _.map(groups, function(group) {
return {
clientId: group[0].clientId,
name: group[0].name,
address1: group[0].address1,
address2: group[0].address2,
deliveries: [{
invoiceNumber: _.map(group,'invoiceNumber'),
itemDescription: _.map(group,'itemDescription'),
itemQuantity: _.map(group,'itemQuantity'),
}]
}
});
And it produces the following output:
[ { clientId: 'FOO',
name: 'John Doe',
address1: 'London',
address2: '1 High Street',
deliveries: [ [Object] ] } ]
I am not sure how to create an array of "deliveries" objects within the lodash _.map function. Please help! :)

Use one map that picks out the properties you want from a group:
...
deliveries: _.map(group, item => _.pick(item, ['invoiceNumber', 'itemDescription', 'itemQuantity' ]))
...

You weren't far off!
Had a go at making it a little bit more succinct by using ES6 syntax and incorporating _.pick() into your code, as suggested by Gruff.
If you really like one-liners, that's possible, too! Lodash makes it easy to chain/nest functions to do this, although you may compromise some readability:
// Data:
const rows = [{
"clientId": "FOO",
"name": "John Doe",
"address1": "London",
"address2": "1 High Street",
"invoiceNumber": "1234",
"itemDescription": "Item 1",
"itemQuantity": 1
},
{
"clientId": "BAR",
"name": "John Doe",
"address1": "London",
"address2": "1 Oxford Street",
"invoiceNumber": "0987",
"itemDescription": "Item 2",
"itemQuantity": 1
},
{
"clientId": "FOO",
"name": "John Doe",
"address1": "London",
"address2": "1 High Street",
"invoiceNumber": "5678",
"itemDescription": "Item 3",
"itemQuantity": 1
}];
// Result:
const groups = _.groupBy(rows, item => item.clientId);
const result = _.map(groups, group => {
const {clientId, name, address1, address2} = group[0];
const deliveries = _.map(group, item => _.pick(item, ['invoiceNumber', 'itemDescription', 'itemQuantity']));
return {clientId, name, address1, address2, deliveries};
});
console.log('multi-liner result: ', result);
// One-line version:
const oneLiner = _.map(_.groupBy(rows, item => item.clientId), group => _.merge(_.pick(group[0], ['clientId', 'name', 'address1', 'address2']), {deliveries: _.map(group, item => _.pick(item, ['invoiceNumber', 'itemDescription', 'itemQuantity']))}));
console.log('one-liner result: ', oneLiner);
<script src="https://cdn.jsdelivr.net/lodash/4.17.1/lodash.min.js"></script>
Hope that helps! If you'd like more info on the Lodash functions used, here's the official documentation for them:
_.groupBy()
_.map()
_.pick()

Related

Create array of objects for a select based on items from a data table

So I have a data table in Vuetify that's containing eventlog items. For example, it looks like this:
[{
"id": 1,
"actionBy": "John",
"eventAction": "CREATED",
"Description": ""
"actionDate": "2022-09-02T10:31:57.223765"
}, {
"id": 2,
"actionBy": "Anna",
"eventAction": "MODIFIED",
"Description": ""
"actionDate": "2022-09-07T13:44:29.892831"
}, {
"id": 3,
"actionBy": "Eric",
"eventAction": "REMOVE_IMAGE",
"Description": "Test description"
"actionDate": "2022-09-07T13:44:39.800381"
}, {
"id": 4,
"actionBy": "Sysadmin",
"eventAction": "REMOVE_IMAGE",
"Description": "Test description"
"actionDate": "2022-09-08T09:21:29.272312"
}, {
"id": 5,
"actionBy": "Sysadmin",
"eventAction": "MODIFIED",
"Description": "Test description"
"actionDate": "2022-09-08T09:21:54.991851"
}, {
"id": 6,
"actionBy": "Sysadmin",
"eventAction": "REMOVE_IMAGE",
"Description": "Test description"
"actionDate": "2022-09-08T13:55:00.469676"
}
]
Now I want to use a select(in Vuetify it's a v-select). Where I want to get the select options like this:
[
{ value: 'CREATED', count: 1 },
{ value: 'MODIFIED', count: 2 },
{ value: 'REMOVE_IMAGE', count: 3 },
]
I found some ways, but it doesn't feel really clean. I want to know if there is a efficient way to do this. So it doesn't look dirty. ;-)
You can use a group by from here and a mapping from here to create an array:
computed: {
selectOptions() {
const obj = this.events.reduce((ar, x) => {
ar[x.eventAction] = (ar[x.eventAction] || 0) + 1;
return ar;
}, {});
return Object.keys(obj).map(key => ({value: key, count: obj[key]}));
}

I need to sort my data based on date at user level

I have this data which is in multiple email format, I have many test users for which I want to match with one and get that user's Date to sort my data. The data is:
[
{
_id: "60642127b982sa55299q674444a",
subject: "Email 1",
body: "Body 1",
to: [
{
email_id: "test1#gmail.com",
name: "test 1",
date: "2021-10-01 12:00:00"
},
{
email_id: "test2#gmail.com",
name: "test 2",
date: "2021-10-01 13:00:00"
}
],
cc: [
{
email_id: "test3#gmail.com",
name: "test 3",
date: "2021-10-01 14:00:00"
},
{
email_id: "test4#gmail.com",
name: "test 4",
date: "2021-10-01 15:00:00"
}
],
bcc: [
{
email_id: "test5#gmail.com",
name: "test 5",
date: "2021-10-01 16:00:00"
},
{
email_id: "test6#gmail.com",
name: "test 6",
date: "2021-10-01 17:00:00"
},
{
email_id: "test2#gmail.com",
name: "test 2",
date: "2021-10-01 13:00:00"
}
]
},
{
_id: "60642127b982sa55299q674444b",
subject: "Email 2",
body: "Body 2",
to: [
{
email_id: "test1#gmail.com",
name: "test 1",
date: "2021-10-01 12:10:00"
},
{
email_id: "test2#gmail.com",
name: "test 2",
date: "2021-10-01 13:10:00"
}
],
cc: null,
bcc: [
{
email_id: "test5#gmail.com",
name: "test 5",
date: "2021-10-01 16:10:00"
},
{
email_id: "test6#gmail.com",
name: "test 6",
date: "2021-10-01 17:10:00"
}
]
},
{
_id: "60642127b982sa55299q674444c",
subject: "Email 3",
body: "Body 2",
to: [
{
email_id: "test1#gmail.com",
name: "test 1",
date: "2021-10-01 12:10:00"
},
{
email_id: "test2#gmail.com",
name: "test 2",
date: "2021-10-01 13:15:00"
}
],
cc: null,
bcc: null
},
{
_id: "60642127b982sa55299q674444d",
subject: "Email 4",
body: "Body 2",
to: [
{
email_id: "test1#gmail.com",
name: "test 1",
date: "2021-10-01 12:10:00"
},
{
email_id: "test3#gmail.com",
name: "test 3",
date: "2021-10-01 13:10:00"
}
],
cc: null,
bcc: [
{
email_id: "test2#gmail.com",
name: "test 2",
date: "2021-10-01 12:10:00"
},
{
email_id: "test6#gmail.com",
name: "test 6",
date: "2021-10-01 17:10:00"
}
]
},
{
_id: "60642127b982sa55299q674444e",
subject: "Email 5",
body: "Body 2",
to: [
{
email_id: "test1#gmail.com",
name: "test 1",
date: "2021-10-01 12:15:00"
},
{
email_id: "test3#gmail.com",
name: "test 3",
date: "2021-10-01 14:10:00"
}
],
cc: [
{
email_id: "test2#gmail.com",
name: "test 2",
date: "2021-10-01 16:50:00"
},
{
email_id: "test5#gmail.com",
name: "test 5",
date: "2021-10-01 16:10:00"
}
],
bcc: null
}
]
I am looking for a mongodb query to fetch my userID (test2#gmail.com) and sort it based on test2#gmail.com's date. I want my result to be sorted based on date in ascending order as Email 4, Email 1, Email 2, Email 3,Email 5.
[
{
"_id": null,
"uniqueValues": {
"_id": "60642127b982sa55299q674444d",
"body": "Body 4",
"concat": {
"date": "2021-10-01 12:10:00",
"email_id": "test2#gmail.com",
"name": "test 2"
},
"subject": "Email 4"
}
},
{
"_id": null,
"uniqueValues": {
"_id": "60642127b982sa55299q674444a",
"body": "Body 1",
"concat": {
"date": "2021-10-01 13:00:00",
"email_id": "test2#gmail.com",
"name": "test 2"
},
"subject": "Email 1"
}
},
{
"_id": null,
"uniqueValues": {
"_id": "60642127b982sa55299q674444b",
"body": "Body 2",
"concat": {
"date": "2021-10-01 13:10:00",
"email_id": "test2#gmail.com",
"name": "test 2"
},
"subject": "Email 2"
}
},
{
"_id": null,
"uniqueValues": {
"_id": "60642127b982sa55299q674444c",
"body": "Body 3",
"concat": {
"date": "2021-10-01 13:15:00",
"email_id": "test2#gmail.com",
"name": "test 2"
},
"subject": "Email 3"
}
},
{
"_id": null,
"uniqueValues": {
"_id": "60642127b982sa55299q674444e",
"body": "Body 5",
"concat": {
"date": "2021-10-01 16:50:00",
"email_id": "test2#gmail.com",
"name": "test 2"
},
"subject": "Email 5"
}
}
]
I have used this query,I'm not getting the proper results. some of the documents are skipped. If anyone has any idea, please respond.
db.collection.aggregate([
{
"$match": {
"$or": [
{
"to.email_id": "test2#gmail.com"
},
{
"cc.email_id": "test2#gmail.com"
},
{
"bcc.email_id": "test2#gmail.com"
}
]
}
},
{
"$project": {
subject: 1,
body: 1,
concat: {
$concatArrays: [
"$to",
"$cc",
"$bcc"
]
}
}
},
{
"$unwind": "$concat"
},
{
"$match": {
"concat.email_id": "test2#gmail.com"
}
},
])
Hope , you have expected the following aggregation. I have added / modified few stages with your pipeline.
$match to eliminate unwated documents
$ifNull helps what we need to do when variables is null, So I passed [] empty array if variable is null
$concatArrays to concat all 3 arrays
$unwind to deconstruct the array
$group to reconstruct the array, but without any duplications. Because I added 3 conditions to remove duplicate inside _id
$sort to sort the documents
Here is the code
db.collection.aggregate([
{
"$match": {
"$or": [
{ "to.email_id": "test2#gmail.com" },
{ "cc.email_id": "test2#gmail.com" },
{ "bcc.email_id": "test2#gmail.com" }
]
}
},
{
"$project": {
subject: 1,
body: 1,
concat: {
$concatArrays: [
{ $ifNull: [ "$to", [] ] },
{ $ifNull: [ "$cc", [] ] },
{ $ifNull: [ "$bcc", [] ] }
]
}
}
},
{ "$unwind": "$concat" },
{ "$match": { "concat.email_id": "test2#gmail.com" } },
{
"$group": {
"_id": { _id: "$_id", date: "$concat.date", email: "$concat.email_id" },
"body": { "$first": "$body" },
"concat": { "$first": "$concat" },
"subject": { "$first": "$subject" }
}
},
{ "$sort": { "concat.date": 1 } },
{ $addFields: { _id: "$_id._id" } }
])
Working Mongo playground
The $concatArrays operator requires all of its inputs to be arrays.
From the docs:
If any argument resolves to a value of null or refers to a field that is missing, $concatArrays returns null.
For those documents where the cc field is null, the result of $concatArrays assigned to the concat field will be null.
This means that the field concat.email_id will not exist, and therefore will not match.
You might use the $ifNull operator to replace any null values with an empty array, like
{$ifNull: [ "$cc", [] ]}

How to create a JSON structure in ReactJS

I have a use-case to create a JSON structure in React in order to POST an API request. The JSON structure body contains objects and arrays.
Please let me know how to create it in ReactJS.
Below is the sample JSON structure that needs to be created using ReactJS:-
{
"transactionAmount": {
"currency": "INR",
"value": 1220.38
},
"transactionDate": "2020-05-18T00:00:00Z",
"tripData": {
"agencyBooked": false,
"legs": [
{
"endLocation": {
"countryCode": "IN",
"city": "Delhi",
"name": "Indira Gandhi International"
},
"startDate": "2020-05-22",
"startTime": "08:00",
"returnLeg": false,
"startLocation": {
"countryCode": "US",
"city": "San Francisco",
"name": "San Francisco International"
},
"endTime": "21:00",
"endDate": "2020-05-22",
"startLocationDetail": "none"
},
{
"endLocation": {
"countryCode": "US",
"city": "San Francisco",
"name": "San Francisco International"
},
"returnLeg": true,
"startDate": "2020-05-24",
"startLocation": {
"countryCode": "IN",
"city": "Delhi",
"name": "Indira Gandhi International"
},
"startTime": "17:00"
}
],
"segmentType": {
"category": "REQ_SEG_AIRFR",
"code": "AIRFR"
},
"selfBooked": false,
"tripType": "ROUND_TRIP"
}
}
You can simply make a JSON format like this.
const dummyObject = {
name: 'Dummy',
age: 22,
about: {
hobbies: ['soccer']
},
works: true
}

How to get the length of a JSON object stored in localStorage?

I have a REACT JS app where a JSON object CartObj stored in localStorage in this format:
{
"Master Automotives": [
{
"SparePartID": "43",
"Name": "Oil and Lubricants",
"Price": "4500",
"VendorID": "48",
"CompanyName": "Master Automotives",
"Qty": 1,
"TotalPrice": "4500"
},
{
"SparePartID": "45",
"Name": "Lights",
"Price": "2300",
"VendorID": "48",
"CompanyName": "Master Automotives",
"Qty": 1,
"TotalPrice": "2300"
}
],
"Repair Solutions": [
{
"SparePartID": "47",
"Name": "Steering Wheel",
"Price": "1500",
"VendorID": "60",
"CompanyName": "Repair Solutions",
"Qty": 1,
"TotalPrice": "1500"
}
],
"FiveStar Automotives": [
{
"SparePartID": "51",
"Name": "Brakes",
"Price": "234",
"VendorID": "70",
"CompanyName": "FiveStar Automotives",
"Qty": 1,
"TotalPrice": "234"
},
{
"SparePartID": "53",
"Name": "Clutch",
"Price": "999",
"VendorID": "70",
"CompanyName": "FiveStar Automotives",
"Qty": 1,
"TotalPrice": "999"
},
{
"SparePartID": "55",
"Name": "LED",
"Price": "288",
"VendorID": "70",
"CompanyName": "FiveStar Automotives",
"Qty": 1,
"TotalPrice": "288"
}
]
}
I want to get the Length of this above JSON Object which is stored in my localStorage as "CartObj". As we can see the above data has total 6 (six) distinct products so I want to get a length equal to 6.
i used the code below but it gets a very big value like 23876:
const lenObj = JSON.stringify(localStorage.getItem('storeObj'));
const len = lenObj.length;
console.log("JSON obj length: ",len);
Please help me how to get the length of this JSON Object in a correct way.
localStorage.getItem returns a string so you have to use JSON.parse not JSON.stringfy
And one more thing that your json is not a single array, it contains multiple arrays, if you want to count all length, you should iterate objects.
Here is the solution.
const lenObj = JSON.parse(localStorage.getItem('storeObj'));
var count = 0;
for(var item in lenObj) {
count += lenObj[item].length
}
console.log("JSON obj length: ",count);
Use this:
const cart = JSON.parse(localStorage.getItem('storeObj'));
const cartLength = Object.values(cart).flat().length;
Or in one line:
const cartLength = Object.values(JSON.parse(localStorage.getItem('storeObj'))).flat().length;
You can use Object.values:
function getLength(obj) {
return (Object.values(obj)).flat().length
}
console.log(getLength(obj)); // => 6
const obj = {
'Master Automotives': [
{
SparePartID: '43',
Name: 'Oil and Lubricants',
Price: '4500',
VendorID: '48',
CompanyName: 'Master Automotives',
Qty: 1,
TotalPrice: '4500',
},
{
SparePartID: '45',
Name: 'Lights',
Price: '2300',
VendorID: '48',
CompanyName: 'Master Automotives',
Qty: 1,
TotalPrice: '2300',
},
],
'Repair Solutions': [
{
SparePartID: '47',
Name: 'Steering Wheel',
Price: '1500',
VendorID: '60',
CompanyName: 'Repair Solutions',
Qty: 1,
TotalPrice: '1500',
},
],
'FiveStar Automotives': [
{
SparePartID: '51',
Name: 'Brakes',
Price: '234',
VendorID: '70',
CompanyName: 'FiveStar Automotives',
Qty: 1,
TotalPrice: '234',
},
{
SparePartID: '53',
Name: 'Clutch',
Price: '999',
VendorID: '70',
CompanyName: 'FiveStar Automotives',
Qty: 1,
TotalPrice: '999',
},
{
SparePartID: '55',
Name: 'LED',
Price: '288',
VendorID: '70',
CompanyName: 'FiveStar Automotives',
Qty: 1,
TotalPrice: '288',
},
],
};
function getLength(obj) {
return (Object.values(obj)).flat().length
}
console.log(getLength(obj));

Angular Differences Between Two Models

I have an angular application which uses a significantly large shared model. Currently, when a user presses save the entire model is posted to a RESTful service. Ideally I would only like to post the fields that have changed. Since this is a shared model I do not have access to form validation states such as dirty/pristine etc. The idea I can think of is to have two models, the original and the modified and compare these.
Original Model
{
"firstName": "John",
"lastName": "Smith",
"isAlive": true,
"age": 25,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021-3100"
},
"phoneNumbers": [
{
"id": "123",
"number": "212 555-1234"
},
{
"id": "456",
"number": "646 555-4567"
},
{
"id": "789",
"number": "123 456-7890"
}
],
"children": [],
"spouse": null
}
Changed Model
{
"firstName": "Jane",
"lastName": "Smith",
"isAlive": true,
"age": 50,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021-3100"
},
"phoneNumbers": [
{
"id": "123",
"number": "1234567890"
},
{
"id": "456",
"number": "646 555-4567"
},
{
"id": "789",
"number": "123 456-7890"
}
],
"children": [],
"spouse": null
}
Data Posted - This is what I need!
{
"firstName": "Jane",
"age": 50,
"phoneNumbers": [
{
"id":"123",
"number": "1234567890"
}
]
}
How can I achieve this? I need the changed fields including any fields called id!
You'll need something like this. Working Fiddle: https://jsfiddle.net/jyyyLaot/
function filter(obj1, obj2) {
var result = {};
for(key in obj1) {
if(obj2[key] != obj1[key]) result[key] = obj2[key];
if(typeof obj2[key] == 'array' && typeof obj1[key] == 'array')
result[key] = arguments.callee(obj1[key], obj2[key]);
if(typeof obj2[key] == 'object' && typeof obj1[key] == 'object')
result[key] = arguments.callee(obj1[key], obj2[key]);
}
return result;
}

Resources