Laurengineer
Laurengineer

Reputation: 747

Appending entries into a nested array

Essentially, I' trying to end up with an array that looks like this:

{
    "projects" : [{
        "project" : "Project One",
        "tasks" : [{
            "name" : "Task One"
        }, {
            "name" : "Task Two"
        }, {
            "name" : "Task Three"
        }]
    }, {
        "project" : "Project Two",
        "tasks" : [{
            "name" : "Task One"
        }, {
            "name" : "Task Two"
        }]
    }]
}

I currently have the following array and a script which look at each element of tasks and checks to see that it's not already in a list of project names, then inserts the first one if it's not, and should add a second element if it is, but that's where I'm a little stuck:

var tasks = [{
        "name" : "Task One",
        "project" : "Project One"
    }, {
        "name" : "Task One",
        "project" : "Project Two"
    }, {
        "name" : "Task Two",
        "project" : "Project One"
    }, {
        "name" : "Task Three",
        "project" : "Project One"
    }, {
        "name" : "Task Two",
        "project" : "Project Two"
    }]


var projectNames = [];
var projects = [];
for ( i = 0; i < tasks.length; i++) {
    if (projectNames.indexOf(tasks[i].project) < 0) {
        // checks to see that it's not the projectNames list, and inserts the first entry
        projectNames.push(tasks[i].project);
        projects.push({
            project : tasks[i].project,
            tasks : {
                name : tasks[i].name
            }
        });
    } else {
        // if that project is already in the list, append the next entry
        var ind = projectNames.indexOf(tasks[i].project);
        projects[ind].tasks.push({
            name : tasks[i].name
        });
    }
}

I'm under the impression that projects[ind].tasks.push() can't be used, but I'm not sure what the correct thing to replace it with would be?

(Equally if there are any better methods of getting from the first array to the second, I'm always happy to learn something new)

Upvotes: 2

Views: 154

Answers (3)

RobG
RobG

Reputation: 147503

Your problem is here:

projects.push({ project: tasks[i].project, tasks: { name: tasks[i].name } });

You are adding tasks as an object, not an array. You should add it as an array with the object as its sole element:

projects.push({ project: tasks[i].project, tasks: [{ name: tasks[i].name }] });

Below is an alternative solution that might be "better" for some criteria, but for maintainability I think the OP is fine. I've changed the names of the tasks to show that the tasks are assigned to the correct project.

var tasks = [{"name" : "Proj one, Task One", "project" : "Project One"},
             {"name" : "Proj two, Task One", "project" : "Project Two"},
             {"name" : "Proj one, Task Two", "project" : "Project One"},
             {"name" : "Proj one, Task Three", "project" : "Project One"},
             {"name" : "Proj two, Task Two", "project" : "Project Two"}
            ];

var projectNames = [];
var groupedTasks = tasks.reduce(function(acc, obj) {
                     var projectName = obj.project;
                     var idx = projectNames.indexOf(projectName);
                     if (idx == -1) {
                       projectNames.push(projectName);
                       idx = acc.projects.length;
                       acc.projects.push({project:projectName,tasks:[]});
                     }
                     acc.projects[idx].tasks.push({name:obj.name});
                     return acc;
                   }, {projects:[]});
                   
console.log(groupedTasks);

Upvotes: 1

Jose Rocha
Jose Rocha

Reputation: 1115

Since this question is already with a correct answer, i will leave here a function for group by that can be useful to you or anyone that passes here in the future.

 var tasks = {
     "tasks": [{
        "name": "Task One",
        "project": "Project One"
     }, {
        "name": "Task One",
        "project": "Project Two"
     }, {
        "name": "Task Two",
        "project": "Project One"
     }, {
        "name": "Task Three",
        "project": "Project One"
     }, {
        "name": "Task Two",
        "project": "Project Two"
     }]
  };

function groupBy(array, f_gb, f_content) {
    var groups = {};
    array.forEach(function(o) {
        var group = f_gb(o);
        groups[group] = groups[group] || [];
        if(f_content == null)
          groups[group].push(o);
        else
          groups[group].push(f_content(o));
    });
    return groups;
}

//An you call the group by like this:
var result = groupBy(tasks.tasks, 
    // function from what to filter from 
    function(item) {
        return item.project;
    }, 
    // optional: what to chose from the object filtered
    function(item){
        return { name : item.name};
    }
);

console.log(result);

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386786

You could use Array#forEach and for thisArg an object, if the same project is already created.

var object = { "tasks": [{ "name": "Task One", "project": "Project One" }, { "name": "Task One", "project": "Project Two" }, { "name": "Task Two", "project": "Project One" }, { "name": "Task Three", "project": "Project One" }, { "name": "Task Two", "project": "Project Two" }] },
    result = { projects: [] };

object.tasks.forEach(function (a) {
    if (!this[a.project]) {
        this[a.project] = { project: a.project, task: [] };
        result.projects.push(this[a.project]);
    }
    this[a.project].task.push({ name: a.name });
}, Object.create(null));

console.log(result);

Upvotes: 2

Related Questions