I have difficulties finding a way to map an array of objects, to a new array that will need to have objects defined by specific fields and values plus I also need to add objects by a single day, will explain details further down and I cannot use for loops of any kind due code style restriction I have in my project
The data I need to map to a new array
[
{
id: 'C12-TBX4',
studyId: 'TBX4',
siteId: 'USA-1',
statusType: 'INCOMPLETE',
statusFrom: '2020-12-01',
statusTo: '2020-12-05'
},
{
id: 'C13-TBX4',
studyId: 'TBX4',
siteId: 'USA-1',
statusType: 'INCOMPLETE',
statusFrom: '2020-12-03',
statusTo: '2020-12-07'
},
{
id: 'C14-TBX4',
studyId: 'TBX4',
siteId: 'USA-1',
statusType: 'INCOMPLETE',
statusFrom: '2020-12-05',
statusTo: '2020-12-08'
},
{
id: 'C15-TBX4',
studyId: 'TBX4',
siteId: null,
statusType: 'REJECTED',
statusFrom: '2020-12-05',
statusTo: '2020-12-08'
},
{
id: 'C16-TBX4',
studyId: 'TBX4',
siteId: null,
statusType: 'REJECTED',
statusFrom: '2020-12-05',
statusTo: '2020-12-09'
},
{
id: 'C17-TBX4',
studyId: 'TBX4',
siteId: 'USA-1',
statusType: 'DROPOUT',
eligible: true,
statusFrom: '2020-12-05',
statusTo: '2020-12-09'
},
{
id: 'C17-TBX4',
studyId: 'TBX4',
siteId: 'USA-1',
statusType: 'DROPOUT',
eligible: false,
statusFrom: '2020-12-05',
statusTo: '2020-12-10'
}
]
The above array needs to be compared and re-mapped using the following dates
[
2020-12-01T00:00:00.000Z,
2020-12-02T00:00:00.000Z,
2020-12-03T00:00:00.000Z,
2020-12-04T00:00:00.000Z,
2020-12-05T00:00:00.000Z,
2020-12-06T00:00:00.000Z,
2020-12-07T00:00:00.000Z,
2020-12-08T00:00:00.000Z,
2020-12-09T00:00:00.000Z
]
The dates are in a range from the minimum to the maximum date of the data object.
The data object contains an interval as statusFrom
and statusTo
; I need to have a new array of objects where we will have a single day from the dates object.
The array also will include a new field called total
which is the total of id
in a single study with the same statusType
on the same day.
To give an example of the result I need to have
[
// INCOMPLETE
{
"studyId": "TBX4",
"siteId": "USA-1",
"day": "2020-12-01",
"statusType": "INCOMPLETE",
"total": 1 // Only "id": "C12-TBX4",
},
{
"studyId": "TBX4",
"siteId": "USA-1",
"day": "2020-12-02",
"statusType": "INCOMPLETE",
"total": 1 // Only "id": "C12-TBX4",
},
{
"studyId": "TBX4",
"siteId": "USA-1",
"day": "2020-12-03",
"statusType": "INCOMPLETE",
"total": 2 // we have C13-TBX4 C12-TBX4, dates are overlapping
},
{
"studyId": "TBX4",
"siteId": "USA-1",
"day": "2020-12-03",
"statusType": "INCOMPLETE",
"total": 2 // we have C13-TBX4 C12-TBX4, dates are overlapping
},
{
"studyId": "TBX4",
"siteId": "USA-1",
"day": "2020-12-04",
"statusType": "INCOMPLETE",
"total": 2 // we have C13-TBX4 C12-TBX4, dates are overlapping
},
{
"studyId": "TBX4",
"siteId": "USA-1",
"day": "2020-12-05", // we include only status from and exclude status to
"statusType": "INCOMPLETE",
"total": 2 // we have C13-TBX4 C14-TBX4, dates are overlapping -- C12-TBX4 is excluded
},
{
"studyId": "TBX4",
"siteId": "USA-1",
"day": "2020-12-06",
"statusType": "INCOMPLETE",
"total": 2 // we have C13-TBX4 C14-TBX4, dates are overlapping
},
{
"studyId": "TBX4",
"siteId": "USA-1",
"day": "2020-12-07",
"statusType": "INCOMPLETE",
"total": 1 // we have C14-TBX4
},
]
The above is just the example for the statusType: INCOMPLETE
but the same logic needs to be done for the other statuses.
As you see the goal is to map a new array based on single dates in a range of dates and add the total of how many ids are in that status on that day by a single day.
I do not include any snippets as honestly have no idea where to start and how to do it
CodePudding user response:
If I understand properly, we're given studies that contain a range of days, and we're given a list of specific dates that the studies' ranges encompass. We want to produce study objects that indicate the specific day each contains, and do a little totaling based on matching days and types.
// from the OP
const studyInput = [
{
id: 'C12-TBX4',
studyId: 'TBX4',
siteId: 'USA-1',
statusType: 'INCOMPLETE',
statusFrom: '2020-12-01',
statusTo: '2020-12-05'
},
//...
];
Still not 100% certain from OP comments, but I think the second array are js Date objects (not strings):
// from the OP, clarified
const dateInput = [
new Date('2020-12-01T00:00:00.000Z'),
// ...
];
Do a little preprocessing, converting dates to epoch times, to make the range comparisons quicker and simpler to code:
// prepare input objects for search by date
let studies = studyInput.map(s => {
let r = Object.assign({}, s);
// getTime() gives scalar ms since the epoch, for simpler comparisons
r.statusFrom = new Date(r.statusFrom).getTime();
r.statusTo = new Date(r.statusTo).getTime();
return r;
});
// same for the times
let times = dateInput.map(d => {
let time = d.getTime();
let day = d.toISOString().split('T')[0]; // the day part of the string
return { time, day }
});
Now, walk through the times
, finding the studyInput
s whose range contains the time. For each of those, do a little bookkeeping to count matching day/status and push to the result:
let results = [];
times.forEach(t => {
// get the matching studies, recall that t is an epoch time and a day string
let matchingStudies = studies.filter(s => {
return s.statusFrom <= t.time && t.time < s.statusTo;
});
let totalsByStatus = {};
let matchingResults = matchingStudies.map(ms => {
let r = { day: t.day, studyId: ms.studyId, siteId: ms.siteId, statusType: ms.statusType };
if (!totalsByStatus[ms.statusType]) totalsByStatus[ms.statusType] = 0;
totalsByStatus[ms.statusType] ;
return r;
});
// walk through the matches again, assiging totals and pushing to result
matchingResults.forEach(r => {
r.total = totalsByStatus[r.statusType];
resultObjects.push(r);
})
})
EDIT Updated to achieve the the OP's requirement of combining by type and day...
const data = [
{
id: "C12-TBX4",
studyId: "TBX4",
siteId: "USA-1",
statusType: "INCOMPLETE",
statusFrom: "2020-12-01",
statusTo: "2020-12-05"
},
{
id: "C13-TBX4",
studyId: "TBX4",
siteId: "USA-1",
statusType: "INCOMPLETE",
statusFrom: "2020-12-03",
statusTo: "2020-12-07"
},
{
id: "C14-TBX4",
studyId: "TBX4",
siteId: "USA-1",
statusType: "INCOMPLETE",
statusFrom: "2020-12-05",
statusTo: "2020-12-08"
},
{
id: "C16-TBX4",
studyId: "TBX4",
siteId: null,
statusType: "REJECTED",
statusFrom: "2020-12-05",
statusTo: "2020-12-09"
},
{
id: "C17-TBX4",
studyId: "TBX4",
siteId: null,
statusType: "REJECTED",
statusFrom: "2020-12-05",
statusTo: "2020-12-09"
},
{
id: "C18-TBX4",
studyId: "TBX4",
siteId: "USA-1",
statusType: "DROPOUT",
eligible: true,
statusFrom: "2020-12-05",
statusTo: "2020-12-09"
},
{
id: "C19-TBX4",
studyId: "TBX4",
siteId: "USA-1",
statusType: "DROPOUT",
eligible: false,
statusFrom: "2020-12-05",
statusTo: "2020-12-10"
}
];
const rangeOfDates = [
new Date("2020-12-01T00:00:00.000Z"),
new Date("2020-12-02T00:00:00.000Z"),
new Date("2020-12-03T00:00:00.000Z"),
new Date("2020-12-04T00:00:00.000Z"),
new Date("2020-12-05T00:00:00.000Z"),
new Date("2020-12-06T00:00:00.000Z"),
new Date("2020-12-07T00:00:00.000Z"),
new Date("2020-12-08T00:00:00.000Z"),
new Date("2020-12-09T00:00:00.000Z")
];
// prepare input objects for search by date
let studies = data.map((s) => {
let r = Object.assign({}, s);
// getTime() gives scalar ms since the epoch, for simpler comparisons
r.statusFrom = new Date(r.statusFrom).getTime();
r.statusTo = new Date(r.statusTo).getTime();
return r;
});
// same for the times
let times = rangeOfDates.map((d) => {
let time = d.getTime();
let day = d.toISOString().split("T")[0]; // the day part of the string
return { time, day };
});
let resultIndex = {};
times.forEach((t) => {
// get the matching studies, recall that t is an epoch time and a day string
let matchingStudies = studies.filter(s => {
return s.statusFrom <= t.time && t.time < s.statusTo;
});
matchingStudies.forEach(ms => {
let r = {
day: t.day,
studyId: ms.studyId,
siteId: ms.siteId,
statusType: ms.statusType,
total: 0
};
let key = `${r.day}${r.statusType}`;
if (!resultIndex[key]) resultIndex[key] = r;
resultIndex[key].total ;
});
});
let result = Object.values(resultIndex);
console.log(result);