moaningalways
moaningalways

Reputation: 461

javascript nested forEach, typeError

I am doing double forEach, but getting a typeError, while trying to reconstruct another object:

const users = [
    {teacher: [
        {file: 'chemistry', size: '2MB'},
        {file: 'math', size: '1MB'}]
    }, {student: [
        {file: 'chemistry', size: '3MB'},
        {file: 'math', size: '4MB'}]
    }
];

let final = {};

users.forEach(function(i) {

    i.forEach(function(j){
        let filesizestring = 'newfilesize'+j.size;
        final[j] = j;
        final.j[j.file] = filesizestring;
    })
})

and the expected result is:

{teacher: {
        chemistry: 'newfilesize2MB',
        math: 'newfilesize1MB'
    }, 
student: {
        chemistry: 'newfilesize3MB',
        math: 'newfilesize4MB'
    }
}

could somebody help me fix this?

update

if nested forEach is not possible, how can i achieve the same result?

Upvotes: 1

Views: 125

Answers (7)

Ele
Ele

Reputation: 33726

You're executing a loop over i -> i.forEach(function(j), however, i is an object, so you need to loop over the objects key-value pairs using for (key in i) and for each key get the array as follow i[key]

const users = [{
  teacher: [{
      file: 'chemistry',
      size: '2MB'
    },
    {
      file: 'math',
      size: '1MB'
    }
  ]
}, {
  student: [{
      file: 'chemistry',
      size: '3MB'
    },
    {
      file: 'math',
      size: '4MB'
    }
  ]
}];

let final = {};

users.forEach(function(i) {
  for (key in i) {
    var obj = {};
    i[key].forEach(function(j) {
      let filesizestring = 'newfilesize' + j.size;
      obj[j.file] = filesizestring;
    });
    final[key] = obj;
  }
});

console.log(final);

Hope it helps!

Upvotes: 4

mdatsev
mdatsev

Reputation: 3879

You can do it using reduce like this:

const users = [{
    teacher: [{
            file: 'chemistry',
            size: '2MB'
        },
        {
            file: 'math',
            size: '1MB'
        }
    ]
}, {
    student: [{
            file: 'chemistry',
            size: '3MB'
        },
        {
            file: 'math',
            size: '4MB'
        }
    ]
}];

let final = users.reduce((a, u) => (k = Object.keys(u)[0], { ...a,
    [k]: u[k].reduce((a, s) => ({ ...a,
        [s.file]: 'newfilesize' + s.size
    }), {})
}), {})

console.log(final)

Upvotes: 3

tevemadar
tevemadar

Reputation: 13195

I just do not like not-having a function, even if it has a silly name.
Otherwise the issue is what appears in other answers too: your top-level variable users is an array, and has forEach. But this array contains objects, which have no forEach, but you can use for in instead (that is supposed to be a link) to get teacher/student. And the last level is again an array, which you can address with forEach.
So there is a nested forEach after all, just it is nested into a for in.

const users = [
    {teacher: [
        {file: 'chemistry', size: '2MB'},
        {file: 'math', size: '1MB'}]
    },
    {student: [
        {file: 'chemistry', size: '3MB'},
        {file: 'math', size: '4MB'}]
    }
];

function magic(coll){
  var res={};
  coll.forEach(function(user){
    for(misc in user){
      user[misc].forEach(function(subject){
        this[subject.file]="newfilesize"+subject.size;
      },res[misc]={});
    }
  });
  return res;
}

console.log(magic(users));

(If someone has a good word for misc, please suggest)

Upvotes: 1

Ali Faris
Ali Faris

Reputation: 18592

i is object not array in forEach so you cannot call forEach on object , what you can do is get the array from the object and apply forEach on that array

    const users = [
        {teacher: [{ file: 'chemistry', size: '2MB' },{ file: 'math', size: '1MB' }]}, 
        {student: [{ file: 'chemistry', size: '3MB' },{ file: 'math', size: '4MB' }]}
    ];

    let final = {};

    users.forEach(function (user) {
        let key = Object.keys(user)[0];
        let obj = {};
        user[key].forEach(function(file){
            obj[file.file] = 'newfilesize' + file.size;
        });

        final[key] = obj;
    });

    console.log(final);

Upvotes: 1

Jonas Wilms
Jonas Wilms

Reputation: 138267

I would change the datastructure to this (your current one makes no sense):

  const users = [
   {
     type:"teacher",
     files: [
       {file: 'chemistry', size: '2MB'},
       {file: 'math', size: '1MB'}
     ]
   }, {
     type:"student",
     files: [
       {file: 'chemistry', size: '3MB'},
       {file: 'math', size: '4MB'}
     ]
   }
 ];

Now you can iterate over the users and its files:

for(const user of users){
  for(const file of user.files){
    file.size = "newfilesize" + file.size;
  }
 }

Upvotes: 0

I wrestled a bear once.
I wrestled a bear once.

Reputation: 23379

It's not safe to turn an array into an object like that unless you're sure there is only one item in the array. This answer assumes there may be multiple, and so it returns an array containing the structure you want. If you're sure there will only be one you can just use final[0] as the result array would, in that case, only contain a a single element as well.

const users = [{
  teacher: [{
    file: 'chemistry',
    size: '2MB'
  }, {
    file: 'math',
    size: '1MB'
  }]
}, {
  student: [{
    file: 'chemistry',
    size: '3MB'
  }, {
    file: 'math',
    size: '4MB'
  }]
}];

final = [];
users.forEach(i => {
  let obj = {};
  let name = i.teacher ? "teacher" : "student";
  obj[name] = {};
  i[name].forEach(j => obj[name][j.file] = 'newfilesize' + j.size);
  final.push(obj);
});

console.log(final);

Upvotes: 0

Adrien Brunelat
Adrien Brunelat

Reputation: 4642

Did you try with a data structure like this instead?

const users = [
    {type: "teacher",
     files: [
        {file: 'chemistry', size: '2MB'},
        {file: 'math', size: '1MB'}]
    }, {type: "student",
        files: [
        {file: 'chemistry', size: '3MB'},
        {file: 'math', size: '4MB'}]
    }
];

let final = {};

users.forEach(function(i) {

    i.files.forEach(function(j){
        let filesizestring = 'newfilesize'+j.size;
        final[j] = j;
        final[j][j.file] = filesizestring;  // <-- What?
    })
})

You'll have to explain the line on which I've put a comment though, I don't get it.

Upvotes: 0

Related Questions