Home > other >  How to map data to a new array with specific fields and values and add a single object per day based
How to map data to a new array with specific fields and values and add a single object per day based

Time:07-06

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 studyInputs 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);

  • Related