I have an array of Javascript objects like below.
[
{
email: 'alex#test.com',
fn: 'Alex',
sn: 'McPherson',
phone: '01233xxxxx',
hours: '40',
rate: '20',
amount: '200',
vat: '60',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '1234',
area: 'xzy'
},
{
id: '2345',
area: 'uhj'
}
]
}
},
{
email: 'mike#test.com',
fn: 'Mike',
sn: 'Mann',
phone: '01233xxxxx',
hours: '50',
rate: '70',
amount: '500',
vat: '90',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '1234',
area: 'xzy'
},
{
id: '3456',
area: 'uio'
}
]
}
},
{
email: 'fred#test.com',
fn: 'Fred',
sn: 'Frogg',
phone: '01233xxxxx',
hours: '80',
rate: '90',
amount: '800',
vat: '100',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '4567',
area: 'asdaf'
},
{
id: '3456',
area: 'uio'
}
]
}
},
{
email: 'alex#test.com',
fn: 'Alex',
sn: 'McPherson',
phone: '01233xxxxx',
hours: '90',
rate: '30',
amount: '900',
vat: '120',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '4567',
area: 'asdaf'
},
{
id: '5678',
area: 'asdf'
}
]
}
}
]
What I ideally want is to group those of the same value (shipping.addresses.id) into there own sub array of objects. Expected outcome.
[
{
id: '1234',
area: 'xzy',
data: [
{
email: 'alex#test.com',
fn: 'Alex',
sn: 'McPherson',
phone: '01233xxxxx',
hours: '40',
rate: '20',
amount: '200',
vat: '60',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '1234',
area: 'xzy'
},
{
id: '2345',
area: 'uhj'
}
]
}
},
{
email: 'mike#test.com',
fn: 'Mike',
sn: 'Mann',
phone: '01233xxxxx',
hours: '50',
rate: '70',
amount: '500',
vat: '90',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '1234',
area: 'xzy'
},
{
id: '3456',
area: 'uhj'
}
]
}
}
]
},
{
id: '2345',
area: 'uhj',
data: [
{
email: 'alex#test.com',
fn: 'Alex',
sn: 'McPherson',
phone: '01233xxxxx',
hours: '40',
rate: '20',
amount: '200',
vat: '60',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '1234',
area: 'xzy'
},
{
id: '2345',
area: 'uio'
}
]
}
}
]
},
{
id: '3456',
area: 'uio',
data: [
{
email: 'mike#test.com',
fn: 'Mike',
sn: 'Mann',
phone: '01233xxxxx',
hours: '50',
rate: '70',
amount: '500',
vat: '90',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '1234',
area: 'xzy'
},
{
id: '3456',
area: 'uio'
}
]
}
},
{
email: 'fred#test.com',
fn: 'Fred',
sn: 'Frogg',
phone: '01233xxxxx',
hours: '80',
rate: '90',
amount: '800',
vat: '100',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '4567',
area: 'asdaf'
},
{
id: '3456',
area: 'uio'
}
]
}
}
]
}
]
I can group the input array using a specific attribute using particular key (code below) but I can't seem to get my head around resorting the array based on a key which is array in itself.
Array.from(
data.reduce(
(acc, o) => (acc.get(o.email).push(o), acc),
new Map(data.map( o => [o.email, []] ))
), ([key, value]) => value
)
You can reduce the data array into an object using shipping.addresses.id as keys and return an array using Object.values(). You will need to iterate over the addresses array of each object and create an entry for each id as they are encountered and push to these entries for subsequent elements with the same id.
const byAddressId = Object.values(
data.reduce((a, o) => {
o.shipping.addresses.forEach(({id, area}) => {
a[id] = {...a[id] ?? {id: id, area: area, data: []}};
a[id]['data'].push({...o});
});
return a;
}, {}));
const data = [{"email": "alex#test.com","fn": "Alex","sn": "McPherson","phone": "01233xxxxx","hours": "40","rate": "20","amount": "200","vat": "60","agency": "test","start": "08/06/2017","end": "10/06/2017","shipping": { "addresses": [ { "id": "1234", "area": "xzy" }, { "id": "2345", "area": "uhj" } ]}},{"email": "mike#test.com","fn": "Mike","sn": "Mann","phone": "01233xxxxx","hours": "50","rate": "70","amount": "500","vat": "90","agency": "test","start": "08/06/2017","end": "10/06/2017","shipping": { "addresses": [ { "id": "1234", "area": "xzy" }, { "id": "3456", "area": "uio" } ]}},{"email": "fred#test.com","fn": "Fred","sn": "Frogg","phone": "01233xxxxx","hours": "80","rate": "90","amount": "800","vat": "100","agency": "test","start": "08/06/2017","end": "10/06/2017","shipping": { "addresses": [ { "id": "4567", "area": "asdaf" }, { "id": "3456", "area": "uio" } ]}},{"email": "alex#test.com","fn": "Alex","sn": "McPherson","phone": "01233xxxxx","hours": "90","rate": "30","amount": "900","vat": "120","agency": "test","start": "08/06/2017","end": "10/06/2017","shipping": { "addresses": [ { "id": "4567", "area": "asdaf" }, { "id": "5678", "area": "asdf" } ]}}];
// return array of Object.values from the accumulator
const byAddressId = Object.values(
// reduce the data array into an object with shipping.addresses.id as keys
data.reduce((a, o) => {
// iterate over all addresses for each element
o.shipping.addresses.forEach(({id, area}) => {
// check if an id entry exists, otherwise create one
a[id] = {...a[id] ?? {id: id, area: area, data: []}};
// push the object to the data array of the id object
a[id]['data'].push({...o});
});
return a;
}, {}));
console.log(byAddressId);
That being said, you can use this same method to save yourself two map() calls compared to the group by email example you included in your question.
const byEmail = Object.values(
data.reduce((a, o) => (a[o.email] = [...a[o.email] ?? [], {...o}], a), {}));
I have this aggregation pipeline:
aggregate.lookup({
from: 'tags',
localField: 'tags',
foreignField: '_id',
as: 'tags'
});
aggregate.match({
productType: 'product',
available: true,
categories: {
$elemMatch: {
url: '/category/test'
}
}
});
aggregate.facet({
products: [
{ $sort: { [order]: sort } },
{ $skip: skip },
{ $limit: pageSize },
{
$project: {
_id: 1,
images: 1,
onSale: 1,
price: 1,
quantity: 1,
slug: 1,
sale: 1,
sku: 1,
status: 1,
title: 1,
brand: 1,
tags: 1
}
}
],
tags: [
{ $unwind: '$tags' },
{
$group: {
_id: {
name: '$name.label',
slug: '$slug'
},
count: {
$sum: 1
}
}
}
],
range: [
{
$bucketAuto: {
groupBy: '$price',
buckets: 1,
output: {
min: { $min: '$price' },
max: { $max: '$price' }
}
}
}
],
total: [{ $group: { _id: null, count: { $sum: 1 } } }]
});
aggregate.addFields({
total: {
$arrayElemAt: ['$total', 0]
}
});
aggregate.addFields({
range: {
$arrayElemAt: ['$range', 0]
}
});
Every product has it's own tags and I can't figure out how to:
Get the tags that belong only to the matched products and return an array from the $facet that contains
tags: [{name: 'tag1', slug: 'slug1', count: 10}, {name: 'tag2', slug: 'slug2', count: 5} ]
Where count: 10 are the products that have the tag.
Right now it returns all the tags found in the database.
2. Why the range property returns an object like this:
"range": {
"_id": {
"min": 5.9,
"max": 47
},
"min": 5.9,
"max": 47
}
and not like this since i provide an output object in $bucketAuto:
"range": {
"min": 5.9,
"max": 47
}
As of 2, this is normal mongodb behavior for $bucketAuto.
Here's what i've done and it works:
just right before $facet:
aggregate.lookup({
from: 'tags',
let: { tags: '$tags' },
pipeline: [
{
$match: {
$expr: { $in: ['$_id', '$$tags'] }
}
}
],
as: 'tags'
});
and then inside $facet:
tags: [
{ $unwind: { path: '$tags' } },
{
$group: {
_id: '$tags',
count: {
$sum: 1
}
}
}
],
I am stucked in one aggregate query, Following is my data
Let database = [
{
_id: 'fefesf', name: 'John', info: {date: ISODate(), marks: '12'}
},
{
_id: 'uiuioo', name: 'John', info: {date: ISODate(), marks: '15'}
},
{
_id: 'erygbo', name: 'Ben', info: {date: ISODate(), marks: '18'}
}]
and my aggregate query is
var query = [{
$group: {
_id: '$name',
Marks: {
$push: {
x: '$index', ..............(not working right now)
y: '$info.marks'
}
}
}
}]
Is it possible to get index of grouped document as 'x' while pushing it in 'Marks' array. Like Output should be
[
{_id: 'John', Marks: [{x: 1, y: 12}, {x: 2, y: 15}]},
{_id: 'Ben',{x: 1, y: 18}}
]
Thanks in advance.!
Since mongoDB version 3.6 you can use $reduce for that*:
db.collection.aggregate([
{
$group: {
_id: "$name",
Marks: {$push: {y: "$info.marks"}}
}
},
{$project: {
Marks: {
$reduce: {
input: "$Marks",
initialValue: [],
in: {$concatArrays: [
"$$value",
[
{
y: "$$this.y",
x: {$add: [{$size: "$$value"}, 1]}
}
]
]
}
}
}
}
}
])
See how it works on the playground example
*Inspired by this answer
I want to acccess the fields category and specCategory. I applied *ngFor="let x of data" {{x._id.category}} but it is not working. Whats wrong with it?
I am using aggregate functionality to group the categories.
$group: {
_id: {
category: "$category",
specCategory: "$specCategory"
},
min: {$min: '$price'},
max: {$max: 500},
total:{$sum:1}
}
Result:
[ { _id: { category: 'Wasser & Wind', specCategory: 'Surfen' }, min: 49, max: 500, total: 1 }, { _id: { category: 'Reisen', specCategory: 'Hotel' }, min: 49, max: 500, total: 1 } ]
I will need to access the category, specCategory and total value
you miss closing curly brackets.
{ _id: { category: 'Reisen', specCategory: 'Hotel' },
should be
{ _id: { category: 'Reisen', specCategory: 'Hotel' }},
I added plunker take a look on file: app/app.component.ts
I need to automate some API tests with frisby.js, and I'm stuck at accessing to some data which is nested inside JSON response body.
Below is my code
var frisby = require('c:/frisby');
frisby.create('Request available Voyages')
.post('someURL', {
departureVoyage: {
from: "500",
to: "500",
date: '2017-01-07'}})
.inspectJSON()
.afterJSON(function (voyage) {
console.log(voyage.departureVoyage.voyages);}
Below is the output of inspectJSON() function
{ type: 'voyageResponseWsDTO',
departureVoyage:
{ date: '2017-01-07',
voyages:
[ { endTime: '22:30',
quotas:
[ { id: '8796095719239',
limit: 66,
name: 'PROMOTION',
price: '34',
priceCurrency: 'USD' },
{ id: '8796095620935',
limit: 271,
name: 'ECONOMY',
price: '51',
priceCurrency: 'USD' },
{ id: '8796095489863',
limit: 19,
name: 'BUSINESS',
price: '72',
priceCurrency: 'USD' },
{ id: '8796234721095',
limit: 0,
name: 'CAR PROMOTION',
price: '84',
priceCurrency: 'USD' },
{ id: '8796095752007',
limit: 2,
name: 'VIP',
price: '800',
priceCurrency: 'USD' } ],
time: '20:00',
vessel:
{ carriesVehicles: true,
TypeCode: 'FE',
TypeName: 'Ferry' } } ] } }
and below is the output of console.log(voyage.departureVoyage.voyages);
[ { endTime: '22:30',
quotas: [ [Object], [Object], [Object], [Object], [Object] ],
time: '20:00',
vessel:
{ carriesVehicles: true,
TypeCode: 'FE',
TypeName: 'Ferry' } } ]
My problem is, when I try to access content of "quotas" with "console.log(voyage.departureVoyage.voyages.quotas);" I get "undefined" message.
Do you know a way how can I reach data of this "quotas"? Because I need to reach properties of this "quotas", like id of one them.
Thanks
voyage.departureVoyage.voyages is an array, and quotas is in the first item of that array,
So refer it like below for quotas inside it,
voyage.departureVoyage.voyages[0].quotas