Home > Net >  How to transform a list of objects into an outer new object of objects?
How to transform a list of objects into an outer new object of objects?

Time:03-26

Given this collection of courses, there is a list of subjects that are composed of objects. Is it possible to create an aggregation to create a new field newSubjects that maps all subjects by their ids? For example, given this collection how to transform the subjects entries from :

[
  {
    _id: ObjectId("623e2f0cb242ee9367eb3c9f"),
    subjects: [ { id: '1', name: 'math' }, { id: '2', name: 'physics' } ]
  },
  {
    _id: ObjectId("623e2f17b242ee9367eb3ca0"),
    subjects: [ { id: '1', name: 'math' }, { id: '3', name: 'biology' } ]
  }
]

into:

{
  newSubjects: { '1': { id: '1', name: 'math' }, '2': { id: '2', name: 'physics' } }
}
{
  newSubjects: { '1': { id: '1', name: 'math' }, '3': { id: '3', name: 'biology' } }
}

Using JavaScript I could do with this expression:

db.courses.find().forEach(x => print({"newSubjects": Object.fromEntries(new Map(x.subjects.map(s => [s.id, s])))}))

I'm trying to figure out an aggregation to accomplish this but haven't succeeded with $addField and $map yet.

CodePudding user response:

You can try this query:

Only need one aggregate stage to compound the result you want. In this stage you can use a $map to iterate for every value and return a new one. This new one will have k and v fields.

  • k field will be the object key (1, 2, 3...)
  • v field will be the value field (the object with {id:..., name:...})

And wrapped by $arrayToObject to translate k and v to the desired object.

db.collection.aggregate([
  {
    "$project": {
      "_id": 0,
      "newSubjects": {
        "$arrayToObject": {
          "$map": {
            "input": "$$ROOT.subjects",
            "in": {
              "k": "$$this.id",
              "v": {
                "id": "$$this.id",
                "name": "$$this.name"
              }
            }
          }
        }
      }
    }
  }
])

Example here

CodePudding user response:

Another option:

 db.collection.aggregate([
{
"$project": {
  "newSubjects": {
    "$arrayToObject": {
      "$map": {
        "input": "$subjects",
        "as": "s",
        "in": {
          k: "$$s.id",
          v: "$$s"
        }
       }
      }
     }
    }
   }
 ])

Explained:

Map the array with the necessary k,v suitable for arrayToObject to be projected as newSubjects

playground

  • Related