2

I was trying to sort an array of user objects coming back from the database base on their age, but something really strange happened.

The following code isn't working:

async function getAllUsers(){


    let _users = await User.find({});

    //error here, it shows that cannot read age of null, but why is         
    //stats undefined? I thought the async/await already resolved the promise?
    let sorted = _users.sort((a, b) => b.stat.age - a.stat.age)

    return sorted;
}

Here is the working code after a bunch of researches, but I am not exactly sure why this is working

async function getAllUsers(){

    let _users = await User.find({});
    let deepclone = JSON.parse(JSON.stringify(_users))

    let sorted = deepclone.sort((a, b) => b.stat.age - a.stat.age)

    return sorted;
}

I understand that I am creating a brand new object off of _users so that the deepclone loses reference to the _users array object, but how does this help solving the problem?

//=======just to be clear=======//

 let _users = await User.find({})
 console.log(_users)
    /* this logs
   {
     _id: 65a4d132asd,
     stat: { age: 24 }
    }
*/

 //without doing JSON.parse & JSON.stringify
 _users.sort((a,b) => {
     console.log(a)//this logs ---> {_id: 65a4d132asd,stat: { age: 24 }}
     console.log(a.stat)//this logs ---> undefined
  })

  //with JSON.parse & JSON.stringify
  let deepclone = JSON.parse(JSON.stringify(_users))
   deepclone.sort((a, b) => {
   console.log(a)//this logs ---> {_id: 65a4d132asd,stat: { age: 24 }}
   console.log(a.stat)//this logs ---> 24
   })
Estradiaz
  • 3,483
  • 1
  • 11
  • 22
kjunz
  • 111
  • 1
  • 7
  • Have you tried printing `_users` before sorting it? Have you verified that every entry has a non-`null` `stats` property? – Alexander Nied Jun 15 '19 at 04:32
  • yes i console log _user right after and it got all the users. I even console.log a and b inside the callback function of the sort function and i can see everything. BUT, inside the callback of sort, as soon as i log(a.stats), it shows undefined. – kjunz Jun 15 '19 at 04:35
  • I know that this doesn't solve the JavaScript problem but why not let the database do the sorting for you? –  Jun 15 '19 at 04:41
  • 1
    Have you noticed that you're calling `sort` on `_user` (singular) instead of `_users` (plural)? – AarónBC. Jun 15 '19 at 04:41
  • sorry that was a typo i made when i post the questions – kjunz Jun 15 '19 at 04:43
  • @jeff I actually could. but I just wanna find out why this is happening, this has been bugging me for an hour. – kjunz Jun 15 '19 at 04:45
  • Hopefully this resolves your problem. So in summary, pass `{ lean: true }` as an option, or call `toObject()` or `toJSON()` on the `_users` result to get the properties you want. If none of those work, I'll re-open the question. – Patrick Roberts Jun 15 '19 at 05:09
  • @PatrickRoberts Hello Patrick, thank you for your help! But unfortunately, i went through all 3 possible solutions but none of them work....I don't think this is a mongoose problem, because when I log `_user` after the await statement, it came back correctly as an array with all users in there. I am suspecting that the sort function is messing with the asynchronous behavior in my code... – kjunz Jun 15 '19 at 05:22
  • can you please explain: is stats undefined thus age cannot be read or is age null - your error seems to be incosistent. – Estradiaz Jun 15 '19 at 05:36
  • @Estradiaz when i log(a.stats.age) or log(b.stats.age), the error said cannot read property age of null, meaning stats doesn't exist. But when i log just a or b inside of the sort function, it shows the correct user object with every single property right freaking there lol – kjunz Jun 15 '19 at 05:55
  • @kjunz that's the same problem description as the linked questions I provided. Are you _sure_ that `let sorted = _users.toObject().sort(...)` does not work? – Patrick Roberts Jun 15 '19 at 06:00
  • Notice in particular [this comment](https://stackoverflow.com/q/28442920/1541563#comment96135515_28443137) and the explanation in [this answer](https://stackoverflow.com/a/44833913/1541563). – Patrick Roberts Jun 15 '19 at 06:05
  • @PatrickRoberts i think the error points to the solution - as of JSON stringify removes all undefined stats - let me anwser pls: let a = { b: undefined, c: 1 }; JSON.stringify(a) //"{"c":1}" – Estradiaz Jun 15 '19 at 06:06
  • @Estradiaz ah, that's a good point... yes, that is definitely different than what I believed to be the correct reason. – Patrick Roberts Jun 15 '19 at 06:08
  • @PatrickRoberts yes _users.toObject().sort() doesn't work because i got a TypeError saying that _user.toObject is not a function. – kjunz Jun 15 '19 at 06:15
  • Last question then I'll stop bugging you... can you try `let deepclone = _users.map(user => user.toObject());`? – Patrick Roberts Jun 15 '19 at 06:50
  • @PatrickRoberts no worries man I love a discussion, and yes `let deepclone = _users.map(user => user.toObject());` this works. does it have to do with immutability? but how would that be affecting in my case tho? – kjunz Jun 15 '19 at 06:58
  • @kjunz because I forgot that `_users` is a _collection_ of mongo objects, it is not a mongo object itself, so `toObject()` would not be defined on it. I suspect you provided the `lean` option incorrectly if the above works, and I highly recommend doing that instead that because it's more efficient than calling `toObject()` on each of the objects. But in addition, you should also specify the sort order you need using the query so you don't have to sort it yourself. – Patrick Roberts Jun 15 '19 at 07:04
  • In short, what I originally closed your question as a duplicate of was correct, I was just pointing you in the wrong direction with my earlier comment. – Patrick Roberts Jun 15 '19 at 07:06

1 Answers1

0
 // Maybe this line is not correct

 let _users = await User.find({});

 // Try to console _users 

// It's might be the _users was not return a correctly format data, then you use JSON.parse(JSON.stringify(_users)) to stringify the _users, so it works correctly
Liu Lei
  • 1,256
  • 1
  • 9
  • 18
  • User.find({}) returns a promise, so using await in front of this is perfectly fine. And yes I did console log `_user` right after the await statement and `_user` came back with all the user data – kjunz Jun 15 '19 at 05:26
  • @kjunz after console _user , have u try to iterator it ? or sorted by your own – Liu Lei Jun 15 '19 at 05:35
  • yes. logging each user object will give me the correct object, but once i attempt to log any property like user.stats, it shows undefined – kjunz Jun 15 '19 at 05:54
  • @kjunz can u offered some fake data? It's seems that u didn't approch the correct properties – Liu Lei Jun 15 '19 at 06:00
  • nah i got it working with the second code block in my question. just not sure why it work – kjunz Jun 15 '19 at 06:06