I have json data like this:
[{
"Id": 1,
"Name": "Test 1",
"Time": {
"start": "22:00",
"end": "22:30",
"duration": 30
}
}, {
"Id": 2,
"Name": "Test 2",
"Time": {
"start": "22:05",
"end": "22:35",
"duration": 30
}
}, {
"Id": 3,
"Name": "Test 3",
"Time": {
"start": "23:25",
"end": "23:40",
"duration": 15
}
},{
"Id": 4,
"Name": "Test 4",
"Time": {
"start": "22:15",
"end": "22:50",
"duration": 15
}
}
]
And i want get result like this, determine which intersect by Time:
[
["Test 1", "Test 2", "Test 4"],
["Test 3"]
]
I have solution, but it's too complicated and i use 4 loops inside each other
If there is an easier solution, please suggest, thanks!
Try aggregation, I am not sure this will work exact as per your requirement,
$addFields add new field startHour, that splits string hour and minute from string, using $split, and $arrayElemAt return first element means hour
$group by startHour and make array of Name
db.collection.aggregate([
{
$addFields: {
startHour: {
$arrayElemAt: [{ $split: ["$Time.start", ":"] }, 0]
}
}
},
{
$group: {
_id: "$startHour",
Name: { $push: "$Name" }
}
}
])
Playground
Result:
[
{
"Name": ["Test 1","Test 2","Test 4"],
"_id": "22"
},
{
"Name": ["Test 3"],
"_id": "23"
}
]
Related
I have the below array which has a sub array of categories, I would like to output the array over and over but grouping the items into another array based on their related categories
testData2: any = [{
"id": 0,
"name": "XyZ",
"category": [ {
"title": "horse"
}, {
"title": "food"
}]
}, {
"id": 2,
"name": "something 2",
"category": [{
"title": "fishing"
}, {
"title": "horse"
}, {
"title": "food"
}]
},
{
"id": 3,
"name": "something 3",
"category": [{
"title": "horse"
}]
}, {
"id": 4,
"name": "something 4",
"category": [{
"title": "food"
}, {
"title": "beer"
}]
}, {
"id": 5,
"name": "something 4",
"category": [{
"title": "fishing"
}]
}
]
So far I have this which works, but i cant help wonder if there is some new JS magic which may be more perfomant to accomplish this ?
let newArray = [];
for (let x = 0; x < this.testData2.length; x++) {
let parent = this.testData2[x];
let child = parent.category;
for (let y = 0; y < child.length; y++) {
let cat = child[y];
let format = parent
newArray.push({ group_heading: cat.title, services: [format] })
}
}
let finalOutput = newArray.reduce((acc, curr) => {
const ndx = acc.findIndex((e: any) => e.group_heading === curr.group_heading);
if(ndx > -1){
acc[ndx].services.push(...curr.services)
} else{
acc.push(curr)
}
return acc;
}, [])
which outputs this as desired
[{
"group_heading": "horse",
"services": [{
"id": 0,
"name": "XyZ",
"category": [{
"title": "horse"
}, {
"title": "food"
}]
}, {
"id": 2,
"name": "something 2",
"category": [{
"title": "fishing"
}, {
"title": "horse"
}, {
"title": "food"
}]
}, {
"id": 3,
"name": "something 3",
"category": [{
"title": "horse"
}]
}]
}, {
"group_heading": "food",
"services": [{
"id": 0,
"name": "XyZ",
"category": [{
"title": "horse"
}, {
"title": "food"
}]
}, {
"id": 2,
"name": "something 2",
"category": [{
"title": "fishing"
}, {
"title": "horse"
}, {
"title": "food"
}]
}, {
"id": 4,
"name": "something 4",
"category": [{
"title": "food"
}, {
"title": "beer"
}]
}]
}, {
"group_heading": "fishing",
"services": [{
"id": 2,
"name": "something 2",
"category": [{
"title": "fishing"
}, {
"title": "horse"
}, {
"title": "food"
}]
}, {
"id": 5,
"name": "something 4",
"category": [{
"title": "fishing"
}]
}]
}, {
"group_heading": "beer",
"services": [{
"id": 4,
"name": "something 4",
"category": [{
"title": "food"
}, {
"title": "beer"
}]
}]
}]
I would probably do something like this:
// first collect services by category
const servicesByCategory = {}
for(const service of testData2){
for(const {title} of service.category){
if(!servicesByCategory[title]){
servicesByCategory[title] = []
}
servicesByCategory[title].push(data)
}
}
// whip it into whatever form you need
return Object.entries(servicesByCategory)
.map(([group_headings, services]) => ({group_headings, services}))
I have a following data structure, array of dictionaries
[
{
"id": 1,
"name": "Some Name 1",
"topics": [
{
"id": 3,
"name": "Topic Name",
"topics": [],
},
{
"id": 3,
"name": "Topic Name",
"topics": [],
}
]
},
{
"id": 2,
"name": "Some Name 2",
"topics": [
{
"id": 3,
"name": "Topic Name",
"topics": [],
},
{
"id": 3,
"name": "Topic Name",
"topics": [],
}
]
},
[
{
"id": 1,
"name": "Some Name 2",
"topics": [
{
"id": 1,
"name": "Topic Name 1",
"topics": [],
},
{
"id": 2,
"name": "Topic Name 2",
"topics": [],
}
]
},
{
"id": 2,
"name": "Some Name 1",
"topics": [
{
"id": 3,
"name": "Topic Name",
"topics": [],
},
{
"id": 3,
"name": "Topic Name",
"topics": [],
}
]
},
I want to essentially be able to combine the arrays within each dictionary if I have the same id, so for each dictionary i will have higher level dictionary items being the same and essentially combine those together to avoid duplication. Preferably i would like it to be recursively being that in case the topics within topics are also duplicate (you can assume the order is always respected).
I would appreciate if you could share a solution to this
I have a category collection and I want to get category by id with some options. This is collection's structure in database.
[
{
"_id": "5d67296bf35b984e74486924",
"name": "Dinrk",
"images": [],
"recipes": [
{
"name": "Coffee",
"time": 20,
"img": "https://google.com/image-example.jpg",
"des": "This is description",
"serving": 2,
"components": [
{
"name": "Dink 1",
"quantity": "1"
},
{
"name": "Dink 2",
"quantity": "1"
},
{
"name": "Dink 2",
"quantity": "1"
}
],
"cook_steps": [
{
"des": "This is description",
"pictures": []
},
{
"des": "This is description",
"pictures": []
}
]
},
{
"name": "Coffee",
"time": 20,
"img": "https://google.com/image-example.jpg",
"des": "This is description",
"serving": 2,
"components": [
{
"name": "Dink 1",
"quantity": "1"
},
{
"name": "Dink 2",
"quantity": "1"
}
],
"cook_steps": [
{
"des": "This is description",
"pictures": []
},
{
"des": "This is description",
"pictures": []
}
]
}
]
},
{
"_id": "5d67296bf35b984e74486435555",
"name": "Cake",
"images": [],
"recipes": [
{
"name": "Cake",
"time": 20,
"img": "https://google.com/image-example.jpg",
"des": "This is description",
"serving": 2,
"components": [
{
"name": "Cake 1",
"quantity": "1"
},
{
"name": "Cake 2",
"quantity": "1"
},
{
"name": "Cake 2",
"quantity": "1"
}
],
"cook_steps": [
{
"des": "This is description",
"pictures": []
},
{
"des": "This is description",
"pictures": []
}
]
},
{
"name": "Coffee",
"time": 20,
"img": "https://google.com/image-example.jpg",
"des": "This is description",
"serving": 2,
"components": [
{
"name": "Cake 1",
"quantity": "1"
}
],
"cook_steps": [
{
"des": "This is description",
"pictures": []
},
{
"des": "This is description",
"pictures": []
}
]
}
]
}
]
This is my code to try categoryId = "5d67296bf35b984e74486924"
Category.aggregate([
{
$match: {'_id': categoryId}
},
{
$unwind: '$recipes'
},
{
$project: {
'total_components': {'$size': '$recipes.components'},
'total_cook_steps': {'$size': '$recipes.cook_steps'}
}
}
]).then(function(data) {
}, function(err) {
})
And expected result is
{
"_id": "5d67296bf35b984e74486924",
"name": "Dinrk",
"images": [],
"recipes": [
{
"name": "Coffee",
"time": 20,
"img": "https://google.com/image-example.jpg",
"des": "This is description",
"serving": 2,
"total_components": 3,
"total_cook_steps": 2
},
{
"name": "Coffee",
"time": 20,
"img": "https://google.com/image-example.jpg",
"des": "This is description",
"serving": 2,
"total_components": 2,
"total_cook_steps": 2
}
]
}
But when I run above my code, result is [].
If you understand my problem, please help me. I have search a lot, but not found solution. So I want to ask everyone. Thankyou so much.
Your query is not giving you the desired result since Mongoose does not auto-cast the 24 char hex string to ObjectId in its aggregate pipeline since $project and $group can change the schema in surprising ways that it becomes hard to infer what should be an ObjectId.
You need to manually convert the categoryId
string to ObjectId using the mongoose.Types.ObjectId method.
Compute the new fields within a $map operator instead of $unwind as this allows you an aggregate operation with fewer pipeline steps
Category.aggregate([
{ '$match': { '_id': mongoose.Types.ObjectId(categoryId) } },
{ '$addFields': {
'recipes': {
'$map': {
'input': '$recipes',
'in': {
'name': '$$this.name',
'time': '$$this.time',
'img': '$$this.img',
'des': '$$this.des',
'serving': '$$this.serving',
'total_components': { '$size': '$$this.components' },
'total_cook_steps': { '$size': '$$this.cook_steps' }
}
}
}
} }
]).then(function(data) {
}, function(err) {
})
I have an array of object that looks like this:
[
{
"group": {
"name": "Software Developers",
"desc":"some desc"
},
"name": "devs 1",
"file": "f1.csv"
},
{
"group": {
"name": "Software Developers",
"desc":"some desc"
},
"name": "devs 2",
"file": "f2.csv"
},
{
"group": {
"name": "Ceos",
"desc":"some desc"
},
"name": "ceos 1",
"file": "f3.csv"
},
{
"group": {
"name": "Ceos",
"desc":"some desc"
},
"name": "ceos 2",
"file": "f4.csv"
}
]
Using lodash how to transform this json array to be like this:
[
{
"group": {
"name": "Software Developers",
"desc":"some desc"
},
lists: [{ "name": "devs 1", "file": "f1.csv" }, { "name": "devs 2", "file": "f2.csv" }]
},
{
"group": {
"name": "Ceos",
"desc":"some desc"
},
lists: [{ "name": "ceos 1", "file": "f3.csv" }, { "name": "ceos 2", "file": "f4.csv" }]
}
]
I tried various solutions but without any luck.
_.groupBy() the group.name, and then _.map() to the desired structure:
const data = [{"group":{"name":"Software Developers","desc":"some desc"},"name":"devs 1","file":"f1.csv"},{"group":{"name":"Software Developers","desc":"some desc"},"name":"devs 2","file":"f2.csv"},{"group":{"name":"Ceos","desc":"some desc"},"name":"ceos 1","file":"f3.csv"},{"group":{"name":"Ceos","desc":"some desc"},"name":"ceos 2","file":"f4.csv"}];
const result = _.flow([
(arr) => _.groupBy(arr, 'group.name'),
(groups) => _.map(groups, (g) => ({
..._.head(g).group,
lists: _.map(g, o => _.pick(o, ['name', 'file']))
}))
])(data);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
You could also achieve this without lodash via ES6 with Object.entries, reduce & map:
const data = [{ "group": { "name": "Software Developers", "desc": "some desc" }, "name": "devs 1", "file": "f1.csv" }, { "group": { "name": "Software Developers", "desc": "some desc" }, "name": "devs 2", "file": "f2.csv" }, { "group": { "name": "Ceos", "desc": "some desc" }, "name": "ceos 1", "file": "f3.csv" }, { "group": { "name": "Ceos", "desc": "some desc" }, "name": "ceos 2", "file": "f4.csv" } ]
const result = Object.entries(data.reduce((r,c) =>
(r[c.group.name] = [...r[c.group.name] || [], c], r), {}))
.reduce((r,[k,v]) => (r.push({ group: v[0].group, lists: v.map(({name, file}) =>
({ name, file })) }), r), [])
console.log(result)
I have an object structure as given below. I had grouped like this using underscore js based on the id values
{
"1": [
{
"id": 1,
"tailName": "ABQ-PHX",
"itemId": 1,
"name": "a1",
"start": "24-11-2013,2:38",
"end": "29-11-2013,18:22"
},
{
"id": 1,
"tailName": "ABQ-PHX",
"itemId": 9,
"name": "a2",
"start": "16-12-2013,10:46",
"end": "18-12-2013,10:46"
}
],
"2": [
{
"id": 2,
"tailName": "BNA-RDU",
"itemId": 14,
"name": "b1",
"start": "21-11-2013,11:38",
"end": "15-12-2013,7:52"
}
]
}
Please advise how to access the array from the above object.
I want to access the first array (i.e)
[
{
"id": 1,
"tailName": "ABQ-PHX",
"itemId": 1,
"name": "a1",
"start": "24-11-2013,2:38",
"end": "29-11-2013,18:22"
},
{
"id": 1,
"tailName": "ABQ-PHX",
"itemId": 9,
"name": "a2",
"start": "16-12-2013,10:46",
"end": "18-12-2013,10:46"
}
]
Both your_object[1] and your_object["1"] will give you the result.