Home > Enterprise >  Getting rid of a nested for loop to improve performance
Getting rid of a nested for loop to improve performance

Time:07-15

I have a function thats supposed to be merge two objects together based on certain conditions. the vitalsArray is an array of objects that are unsorted that may or may not have corresponding values (i.e systolic to diastolic and vice versa). Basically if certain conditions are met that would mean that the systolic object and the diastolic object are one BP vital and need to be merged.

My question is is there a better way of doing this than a nested forEach loop?

const mergeBloodPressures = (vitalsArray) => {
  let ids = []
  let finalArray = []
  vitalsArray.forEach((tmpBP) => {
    vitalsArray.forEach((bp) => {
      let matchDate = moment(bp.takenOn).isSame(
        moment(tmpBP.takenOn),
        "minute"
      )
      if (
        matchDate &&
        bp.vitalType.name != tmpBP.vitalType.name &&
        !ids.includes(bp.id) &&
        !ids.includes(tmpBP.id)
      ) {
        let temp = {}
        if (bp.vitalType.name == "diastolic blood pressure") {
          temp.diastolic = bp
          temp.systolic = tmpBP
        } else {
          temp.diastolic = tmpBP
          temp.systolic = bp
        }
        ids.push(bp.id)
        finalArray.push(temp)
      }
    })
  })
  return finalArray
}

Sample Input:

[
{
    "patient_id": 716,
    "vital_type_id": 2,
    "value": "78",
    "taken_on": "2022-06-22T14:49:48.948-05:00",
    "vitalType": {
        "id": 2,
        "name": "diastolic_blood_pressure",
        "units": "mmHg",
        "created_at": "2022-06-22T14:40:43.746-05:00",
        "updated_at": "2022-06-22T14:40:43.746-05:00"
    },
    "id": 9101,
},
{
    "patient_id": 716,
    "vital_type_id": 1,
    "value": "129",
    "taken_on": "2022-06-22T14:49:48.948-05:00",
    "vital_type": {
        "id": 1,
        "name": "systolic_blood_pressure",
        "units": "mmHg",
        "created_at": "2022-06-22T14:40:43.740-05:00",
        "updated_at": "2022-06-22T14:40:43.740-05:00"
    },
    "id": 9102,
}
]

Sample Output:

[
{
    "diastolic": {
        "patient_id": 716,
        "vital_type_id": 2,
        "value": "78",
        "taken_on": "2022-06-22T14:49:48.948-05:00",
        "vitalType": {
            "id": 2,
            "name": "diastolic_blood_pressure",
            "units": "mmHg",
            "created_at": "2022-06-22T14:40:43.746-05:00",
            "updated_at": "2022-06-22T14:40:43.746-05:00"
        },
        "id": 9101,
    },
    "systolic": {
        "patient_id": 716,
        "vital_type_id": 1,
        "value": "129",
        "taken_on": "2022-06-22T14:49:48.948-05:00",
        "vitalType": {
            "id": 1,
            "name": "systolic_blood_pressure",
            "units": "mmHg",
            "created_at": "2022-06-22T14:40:43.740-05:00",
            "updated_at": "2022-06-22T14:40:43.740-05:00"
        },
        "id": 9102
    }
}
]

CodePudding user response:

Your input only shows a single patient on a single date but here is an example that extrapolates from that a little to provide a result that is grouped by patient_id and then further grouped by taken_on within that.

The result is of the following shape:

[
  {
    "patient_id": 1,
    "vitals": [
      {
        "diastolic": {
          taken_on: 1,
          //...
        },
        "systolic": {
          taken_on: 1,
          //...
        }
      },
      {
        "diastolic": {
          taken_on: 2,
          //...
        },
        "systolic": {
          taken_on: 2,
          //...
        }
      }
    ]
  },
  {
    "patient_id": 2,
    "vitals": [
      {
        "diastolic": {
          taken_on: 1,
          //...
        },
        "systolic": {
          taken_on: 1,
          //...
        }
      },
      //...
    ]
  }
]

To achieve this the example below iterates each element using a for...of loop then retrieves or creates the a patient_id property using logical nullish assignment (??=). It then further retrieves or initializes an object to group into based on taken_on. Finally the result is created by taking the Object.entries() of the grouped object and mapping over it in order to also convert each taken_on object to a an array of values.

const input = [{ "patient_id": 716, "vital_type_id": 2, "value": "78", "taken_on": "2022-06-22T14:49:48.948-05:00", "vitalType": { "id": 2, "name": "diastolic_blood_pressure", "units": "mmHg", "created_at": "2022-06-22T14:40:43.746-05:00", "updated_at": "2022-06-22T14:40:43.746-05:00" }, "id": 9101, }, { "patient_id": 716, "vital_type_id": 1, "value": "129", "taken_on": "2022-06-22T14:49:48.948-05:00", "vital_type": { "id": 1, "name": "systolic_blood_pressure", "units": "mmHg", "created_at": "2022-06-22T14:40:43.740-05:00", "updated_at": "2022-06-22T14:40:43.740-05:00" }, "id": 9102, }];

const vitalTypesById = {
  1: 'systolic',
  2: 'diastolic'
};

const groupedByPatient = {};
for (const o of input) {
  const takenOn = new Date(o.taken_on).setSeconds(0, 0);

  const patient = (groupedByPatient[o.patient_id] ??= {});
  const patientByDate = (patient[takenOn] ??= {});

  patientByDate[vitalTypesById[o.vital_type_id]] = { ...o };
}

const result = Object.entries(groupedByPatient).map(([patient_id, vitals]) => (
  {
    patient_id,
    vitals: Object.values(vitals)
  }
));

console.log(JSON.stringify(result, null, 2));

CodePudding user response:

You want to group by patientid and takenon. I would suggest running through the vitals array and stuff it into a dictionary based on vital name.

Conceptually you'd do something like so.

var mydictionary[] = {};
for (val in vitalsarray) {
    let key = val.patientId   "."   val.takenOn;
    if (mydictionary[key]) {
        let list = mydictonary[key];
        list.push( val );
    } else {
        let listo = [];
        listo.push( val );
        mydictionary[ key ] = listo;
    }
}

The above is sort a pseudo code, but that gets you the patient stuff grouped to together. If you get a little slicker, you can actually look at the list and have a further dictionary which contains the vitals by type....

CodePudding user response:

The next provided approach utilizes Array.prototype.reduce where the reducer function aggregates/merges related(same) blood pressure data, based on a unique key which itself is a concatenation of a patient's ID (patient_id) and the date/timestamp of measurement (taken_on).

The result is a key based index/map/object of merged related blood pressure data-items (blood pressure data). Passing such an object to Object.values returns an array of said data items.

function aggregateRelatedBloodPressureData(index, dataItem) {
  const { patient_id, taken_on, vital_type } = dataItem;

  const relatedDataKey = `${ patient_id }_${ taken_on }`;
  const pressureType = vital_type.name.split('_')[0] ?? vital_type.name;

  Object
    .assign(
      index[relatedDataKey] ??= {},
      { [pressureType]: dataItem },
    );
  return index;
}

const sampleDate = [{
  patient_id: 716,
  vital_type_id: 2,
  value: "78",
  taken_on: "2022-06-22T14:49:48.948-05:00",
  vital_type: {
    id: 2,
    name: "diastolic_blood_pressure",
    units: "mmHg",
    created_at: "2022-06-22T14:40:43.746-05:00",
    updated_at: "2022-06-22T14:40:43.746-05:00",
  },
  id: 9101,
}, {
  patient_id: 716,
  vital_type_id: 1,
  value: "129",
  taken_on: "2022-06-22T14:49:48.948-05:00",
  vital_type: {
    id: 1,
    name: "systolic_blood_pressure",
    units: "mmHg",
    created_at: "2022-06-22T14:40:43.740-05:00",
    updated_at: "2022-06-22T14:40:43.740-05:00",
  },
  id: 9102,
}];

const aggregatedBloodPressureIndex = sampleDate
  .reduce(aggregateRelatedBloodPressureData, {});

const aggregatedBloodPressureList = Object
  .values(aggregatedBloodPressureIndex);

console.log({
  aggregatedBloodPressureIndex,
  aggregatedBloodPressureList,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }

  • Related