Comparing and counting thousands of dates converted using moment.js I have a very large list of users (a Mongo Collection with roughly +4000 users). Each user belongs to a site. 

On the client-side I need to loop over both the sites and users to get completed and incomplete counts between a startDate and endDate. This logic must take place on the client-side. 

So far I have two for loops and am using moment.js for the time conversions. I noticed that after adding moment, this page took significantly longer to load (about 3 seconds vs. 40ms originally). 

Is there a way I can optimize the code below such that the moment time conversions do not drastically slow down my app?

  function getStatusCounts(sites, users, startDate = new Date(-10000000000000), endDate = new Date(2640000000000000)) {
    startDate = moment.tz(startDate, 'America/Los_Angeles').format('MM/DD/YYYY h:mm a');
    endDate   = moment.tz(endDate, 'America/Los_Angeles').format('MM/DD/YYYY h:mm a');
    var tempDate, tempUserDate;

    for (var i = 0; i &lt; sites.length; i++) {
      for (var j = 0; j &lt; users.length; j++) {
        tempDate = new Date(users[j].userSubmittedAt);
        tempUserDate = moment.tz(tempDate, 'America/Los_Angeles').format('MM/DD/YYYY h:mm a');

        if (sites[i].url === users[j].site_url &amp;&amp; moment(tempUserDate).isAfter(moment(startDate)) &amp;&amp; moment(tempUserDate).isBefore(moment(endDate))) {

          if (users[j].isCompleted === 'true') {
            completed[i]++;
          } else {
            incompleted[i]++;
          }
        }
      }
    }
  }
If you have \$N\$ users and \$M\$ total sites, your two for loops have time complexity \$O(NM)\$. This is a bit high when \$N\$ and \$M\$ are on the order of a couple thousand, and probably explain your performance issues. 

You are wasting a lot of time comparing sites with users with different site_url parameters. You can speed up your code to run in time \$O(N + M)\$ by doing the following:


Build a map siteMap from url values to their index in sites. That is, siteMap should satisfy siteMap[sites[i].url] = i. It is easy to construct this in \$O(M)\$ time by just looping over sites.
Omit the outer for loop over all sites. Instead, just set i in the inner for loop to siteMap[users[j].site_url]. I would also recommend renaming i to something more descriptive in this case, e.g. siteIndex. 


This costs an extra \$O(M)\$ worth of space to store siteMap, but since you have multiple arrays of this size already, this shouldn't be a huge increase in memory burden.

One final note: usually it's good to make the database query do as much of the work as possible instead of filtering it out later manually in the client. One example here where you could possibly speed things up is, instead of checking that the user's time is between startDate and endDate in this JavaScript function, you should adjust your MongoDB query to just return to you the users in this range. 

If you don't have the option of adjusting the MongoDB query (or this would be a bad idea for other reasons, like you need to find the answer quickly for a bunch of ranges and DB calls have high latency), then it would be a good idea to sort your users by their userSubmittedAt parameter. Then you can use a simple binary search to find the appropriate range of users in \$O(\log N)\$ time.