JavaScript - Calculate the streak of consecutive work days

Consider this array of plans:

[
       { _id: "1", project_id: "1", day: "2021-03-02" },
       { _id: "2", project_id: "1", day: "2021-03-01" },
       { _id: "3", project_id: "1", day: "2021-03-03" },
       { _id: "4", project_id: "2", day: "2021-03-01" },
       { _id: "5", project_id: "1", day: "2021-03-04" },
       { _id: "6", project_id: "1", day: "2021-03-10" }
]

The goal is to calculate the consecutive number of days (excluding weekends) for plans with the same project ID. Based on this data, the desired output should be:

[
      { _id: "1", project_id: "1", day: "2021-03-02", count: 4 },
      { _id: "2", project_id: "1", day: "2021-03-01", count: 4 },
      { _id: "3", project_id: "1", day: "2021-03-03", count: 4 },
      { _id: "4", project_id: "2", day: "2021-03-01", count: 1 },
      { _id: "5", project_id: "1", day: "2021-03-04", count: 4 },
      { _id: "6", project_id: "1", day: "2021-03-10", count: 1 }
]

An attempted solution yielded the same count for all plans with the same project ID:

let plans = [
           { _id: "1", project_id: "1", day: "2021-03-02" },
           { _id: "2", project_id: "1", day: "2021-03-01" },
           { _id: "3", project_id: "1", day: "2021-03-03" },
           { _id: "4", project_id: "2", day: "2021-03-01" },
           { _id: "5", project_id: "1", day: "2021-03-04" },
           { _id: "6", project_id: "1", day: "2021-03-10" }
    ];

    plans.filter(p => new Date(p.day).getDay() !== 6 && new Date(p.day).getDay() !== 0).map((plan, i, arr) => {
            let count = 1;
            arr
                .filter(p2 => plan.project_id === p2.project_id)
                .sort((a, b) => new Date(a.day).getTime() - new Date(b.day).getTime())
                .map((plan3, k, arr3) => {
                    if (k > 0) {
                        let tmpDate = new Date(arr3[k - 1].day);
                        if (tmpDate.getDay() === 5) {
                            tmpDate.setDate(tmpDate.getDate() + 3);
                        } else {
                            tmpDate.setDate(tmpDate.getDate() + 1);
                        }
                        if (tmpDate.getTime() === new Date(plan3.day).getTime()) {
                            count++;
                        }
                    }
                });
            plan.count = count;
        })
        
    console.log(plans);

Answer №1

One issue arises when there is a gap, as no new series is created. The condition to test this is:

if (tmpDate.getTime() === new Date(plan3.day).getTime()) {
   count++;
}

However, the problem lies in not explicitly stating that a plan following a gap does not belong to the same series. It is simply not counted, yet the count applies to that plan. Treating a gap similarly to switching projects would be more appropriate.

Additionally, it's inefficient to perform the same analysis for all days within the same project repeatedly. A single loop can handle this without the need for nested loops.

Consider the following approach. While there is indeed a nested loop using .slice....forEach, it operates on a distinct data slice. When considering the total iterations of that inner loop combined, it remains linear rather than quadratic since each plan only engages with the inner loop once.

function nextWorkingDay(a) {
    a = new Date(a); // clone
    let d = a.getDay();
    a.setDate(a.getDate() + (d < 5 ? 1 : 8 - d));
    return a;
}

function addCounts(data) {
    let sorted = [...data].sort((a,b) => 
        a.project_id.localeCompare(b.project_id) || a.day.localeCompare(b.day)
    );

    let count = 0;
    sorted.forEach((o, i, arr) => {
        count++;
        if (i === arr.length - 1 || arr[i+1].project_id != o.project_id 
                                 || +nextWorkingDay(o.day) !== Date.parse(arr[i+1].day)) {
            // Addressing breaks in consecutive days similar to project_id breaks
            arr.slice(i - count + 1, i + 1).forEach(o => o.count = count);
            count = 0;
        }
    });
}

// Sample data
let data = [
       { _id: "1", project_id: "1", day: "2021-03-02" },
       { _id: "2", project_id: "1", day: "2021-03-01" },
       { _id: "3", project_id: "1", day: "2021-03-03" },
       { _id: "4", project_id: "2", day: "2021-03-01" },
       { _id: "5", project_id: "1", day: "2021-03-04" },
       { _id: "6", project_id: "1", day: "2021-03-10" }
];

addCounts(data);
console.log(data);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to change a byte array into an image using JavaScript?

I need assistance converting a byte array to an image using Javascript for frontend display. I have saved an image into a MySQL database as a blob, and it was first converted to a byte array before storage. When retrieving all table values using a JSON ar ...

Optimize Page Speed by Delaying the Loading of Slideshow Images

My main issue with reducing my pagespeed is the slideshow I have implemented. I currently have 17 rather large images in the slideshow, but for simplicity, I will only show the code for 3 images below. I am trying to find a way to load the first image as a ...

What is the process for importing a Kaggle dataset into Elasticsearch?

Having just started with elasticsearch, I am venturing into creating a movie search application. My plan involves sourcing data from kaggle and integrating it into my locally set up elasticsearch at localhost:9200. Upon visiting the localhost link, I came ...

Creating a Dynamic Tree View Component in AngularJS Using JSON Data

I am new to AngularJS and I need help creating a TreeView Structure from a JSON Object. Here is an example of my Return JSON Object: var categoryTree = [{Name:'Item1', Childnodes : {}, id: 1}, {Name:'Item2', Childnod ...

Can JavaScript be used to dynamically assign events to elements on a webpage?

I am currently using the following code: if ( $.support.touch == true ) { $(window).on('orientationchange', function(event){ if ( full == false ) { self.hideAllPanels("7"); } }); } else { $(window).on(&apo ...

When a new marker is clicked on Google Maps, the previous Infowindow will automatically close

Here are some specific locations to consider: var places = [{ "id": 1, "ReferenceNumber": "52525252525", "Address" : "School" , "Latitude": "21.585486", "Longitude": & ...

Sending form data via Ajax for a specific field ID

When sending data to an ajax script, I usually create a form tag and assign it an id like this: <form id="myForm"> Then, in the ajax script, I include the following code: data: $('#myForm').serialize(), This sends all the form data. How ...

Determining the most recent array within an array object in PHP

My goal is to extract data from a JSON object, transform it into an array of objects, and then determine the latest array within this structure based on a specific set of values. For example: // Array structure [ [response] => [ [0] => [ ...

Styling in CSS is being applied to a button element within a React component,

Having trouble with a button styled with the className 'actions' The button displays the CSS styling from '.actions', but not '.actions button'. Both should be applied. This code snippet works for all elements ...

A pair of iterations of the same design

It may sound strange, but I find myself in a situation where I need to have two versions of the same model in my Node.js application. Let me try to explain. I am required to create two different account types, one for job seekers and one for employers. Th ...

Once upon a time in the land of Storybook, a mysterious error message appeared: "Invalid version. You must provide a string

I keep encountering an issue while attempting to set up storybook. Can anyone help me figure out what's causing this problem? npx storybook@latest init • Detecting project type. ✓ TypeError: Invalid version. Must be a string. Got type "obje ...

Traverse a multi-dimensional array fetched from AJAX using jQuery to dynamically fill a dropdown selection menu

I am faced with a multidimensional array returned from PHP. Here is the array I am working with: $carBrands = array("Bmw" => "200" ,"Mercedes" => "201", "Audi" => "202"); My challenge now is how to populate a select dropdown with this informatio ...

Display or conceal nested divs within ng-repeat loop

I set up a div with sub divs to establish a nested grid system. There are three levels altogether: MainDiv - Always Visible SecondDiv - Display or conceal when MainDiv is clicked on ThirdDiv - Display or conceal when SecondDiv is clicked on <div c ...

What is the best way to iterate through this collection of objects?

When I print out the Array in my console, it shows like this: https://i.sstatic.net/7ZVr3.png for (let index = 0; index < employeeList.length; index++) This for loop is not functioning properly because the length is 0. I attempted different approaches ...

Encountering an issue when trying to download a PDF from an Angular 6 frontend using a Spring Boot API - receiving an error related to

When I directly call the Spring Boot API in the browser, it successfully creates and downloads a PDF report. However, when I try to make the same GET request from Angular 6, I encounter the following error: Here is the code snippet for the Spring Boot (Ja ...

Implementing server-side validation measures to block unauthorized POST requests

In my web application using angular and node.js, I am in the process of incorporating a gamification feature where users earn points for various actions such as answering questions or watching videos. Currently, the method involves sending a post request t ...

How can I correctly format a conditional statement within flatMap while using Promise.all in Javascript?

Currently, I am developing a scenario where I use flatMap alongside Promise.all. Within the flatMap function, there are two specific conditions to consider: firstly, checking if the state of the originalObj is false or not before proceeding with the insert ...

Filtering JSON data with JavaScript

Looking to group data in AngularJS using UnderscoreJS. Here is the JSON data: data = [ { "Building*": "Building A", "Wing*": "Wing C", "Floor*": "Floor 3", "Room Name*": "Room 3", "Room Type*": ...

Only when the condition is satisfied in AngularJS will I include JSON data

I have a JSON file named components.json containing information about various board spaces: {"components": { "boardSpaces": [ {"name":"GO!", "price":0, "position":0, "type":"go"}, {"name":"Mediterranean Avenue", "type": "property", "pr ...

Is there a way to modify or add to the response object's methods in Express and Node.js?

When using middleware in Express, the framework passes both a res and a req object. These objects enhance the built-in ones from http.ServerResponse and http.ClientRequest respectively. I am curious about the possibility of overriding or extending methods ...