Paul C
Paul C

Reputation: 340

Nested javascript map functions

I'm working on a MEAN project with handlebars. I originally just used the data direct from my database queries and pushed it to my handlebars views. I've had to stop this as i want to do some data transformation, I also heard it is better security practice as it strips away data you may not intend to pass.

Anyway, I can't get the mapping correct for a nested data set.

My schema is below;

var approverSchema = mongoose.Schema({
    name: String,
    status: String,
    comment: String,
    date: Date
});

var taskSchema = mongoose.Schema({
    name: String,
    description: String,
    owner: String,
    businessUnit: String,
    status: String,  // draft, pending, approved, rejected
    approvalDue: Date,
    submitted: Date,
    approved: Date,
    approvers: [approverSchema]
});

and my code is;

'use strict';
var Task = require('../models/task.js');

exports.home = function (req, res, next) {
    /* get tasks
    ------------------------------------------------------------------ */
    Task.find({
        owner: signedOnUser
    }, function (err, result) { //callback function
        if (err) {
            return console.log(err);
        } 
        var taskContext = {
            tasks: result.map(function(result){
                return {
                    name: result.name,
                    businessUnit: result.businessUnit,
                    status: result.status,
                    approvalDue: result.getDisplayDate(),
                    //approvers: result.approvers
                    approvers: result.approvers.map(function(result){
                        return {
                            name: result.approvers.name
                        };
                    })
                };
            })
        };
        res.render('home', {
            taskContext: taskContext,
        });
    });
};

I'm pretty sure my issue is in the function(result), I'm passing the nested map, but I just can't seem to get it right.

Upvotes: 1

Views: 7675

Answers (2)

codejockie
codejockie

Reputation: 10864

I'm guessing you made a typo in the the map function:

'use strict';
var Task = require('../models/task.js');

exports.home = (req, res, next) => {
    /* get tasks
    ------------------------------------------------------------------ */
    Task.find({
        owner: signedOnUser
    }, function (err, results) { //callback function
        if (err) {
            return console.log(err);
        }

        const taskContext = {
          tasks: results.map((task) => {
              return {
                  name: task.name,
                  businessUnit: task.businessUnit,
                  status: task.status,
                  approvalDue: task.getDisplayDate(),
                  //approvers: task.approvers
                  approvers: task.approvers.map((approver) => {
                      return {
                          name: approver.name
                      };
                  })
              };
          })
      };
        res.render('home', {
            taskContext: taskContext,
        });
    });
};

In my code, I used unique names for the argument passed to each map function in order to prevent conflicting names.
In the second map function I called the name property directly from the approver argument instead of task.approvers.name.

Upvotes: 1

Pavlo
Pavlo

Reputation: 1197

John Kennedy answered the question, but you can clean up the code a bit using object deconstructing. It sounds like you are working server side, so you can use newer JavaScript syntax.

const taskContext = {
  tasks: result.map(({name, businessUnit, status, getDisplayDate, approvers}) => 
                      ({name, 
                        businessUnit, 
                        status, 
                        approvalDue: getDisplayDate(),
                        approvers: approvers.map(({name}) => {name})})
                   );

The deconstructing syntax takes out the properties from the object. Then you can just return them like name, businessUnit and status and they will be set with name:name, businessUnit:businessUnit and status:status, for approvalDue and approvers we need to do a bit of extra work.

Also it is probably a good idea to change approvers: approvers.map(({name}) => {name}) to approvers: approvers.map(({name}) => name), because now its an Array of strings which allowes you to use Array.indexOf and Array.includes instead of Array.find etc.

Upvotes: 0

Related Questions