Home > database >  How do I create an object with a specifically sorted key precedence or key insertion order?
How do I create an object with a specifically sorted key precedence or key insertion order?

Time:02-04

I want to create metadata objects of following structure with focus on such an object's key precedence.

{
  glucose: {
    name: 'Glucose',
    units: 'mg/dL',
  },
  height: {
    name: 'Height',
    units: '"',
    longUnit: 'Inches',
  },
  weight: {
    name: 'Weight',
    units: 'lbs',
    longUnit: 'Pounds',
  },
  bmi: {
    name: 'BMI',
  },
  pulse: {
    name: 'Pulse',
  },
  temperature: {
    name: 'Temperature',
    units: 'F',
    longUnit: 'Fahrenheit',
  },
  respiration_rate: {
    name: 'Respiration Rate',
  },
  o2_saturation: {
    name: 'O2 Saturation',
    units: '%',
  },
}

Each metadata key is related to an item's vital_type.name value of the allVitals array. The key precedence has to follow the item-order of this array when sorted by an item's take_on date-value in descending order.

As for the next following allVitals example the weight value was taken later than the glucose value. Thus I expect the weight key to be inserted before the glucose key.

The allVitals array can have more than just two vital items, it could have vitals of all types and multiple of them (i.e, multiple pulse vitals, multiple weight vitals).

const allVitals = [{
  patient_id: 79,
  vital_type_id: 6,
  value: "9.76",
  taken_on: "2022-11-17T11:07:00.000-06:00",
  vital_type: {
    id: 6,
    name: "glucose",
    units: "mg/dL",
    created_at: "2022-11-17T13:52:00.360-06:00",
    updated_at: "2022-11-17T13:52:00.360-06:00",
  },
  notes: null,
  source: "patient_device",
  id: 1366,
  time_recorded: true,
  severity: "critical_low",
  formatted_severity: "Critical - Low",
  vital_attributes: {},
  vital_status: "valid",
}, {
  patient_id: 79,
  vital_type_id: 4,
  value: "171",
  taken_on: "2022-11-17T13:19:00.000-06:00",
  vital_type: {
    id: 4,
    name: "weight",
    units: "pounds",
    created_at: "2022-11-17T13:52:00.375-06:00",
    updated_at: "2022-11-17T13:52:00.375-06:00",
  },
  notes: null,
  source: "patient_device",
  id: 1399,
  time_recorded: true,
  severity: null,
  formatted_severity: "-",
  vital_attributes: {},
  vital_status: "valid",
}];

The expected new metadata structure then would be ...

{
  weight: {
    name: 'Weight',
    units: 'lbs',
    longUnit: 'Pounds',
  },
  glucose: {
    name: 'Glucose',
    units: 'mg/dL',
  },
  height: {
    name: 'Height',
    units: '"',
    longUnit: 'Inches',
  },
  bmi: {
    name: 'BMI',
  },
  pulse: {
    name: 'Pulse',
  },
  temperature: {
    name: 'Temperature',
    units: 'F',
    longUnit: 'Fahrenheit',
  },
  respiration_rate: {
    name: 'Respiration Rate',
  },
  o2_saturation: {
    name: 'O2 Saturation',
    units: '%',
  },
}

CodePudding user response:

So I solved it with the help of @This Guy so I gave him an upvote

const allVitals = [
  {patient_id:79,vital_type_id:4,value:"171",taken_on:"2022-11-17T13:19:00.000-06:00",vital_type:{id:4,name:"weight",units:"pounds",created_at:"2022-11-17T13:52:00.375-06:00",updated_at:"2022-11-17T13:52:00.375-06:00"},notes:null,source:"patient_device",id:1399,time_recorded:!0,severity:null,formatted_severity:"-",vital_attributes:{},vital_status:"valid"},
  {patient_id:79,vital_type_id:6,value:"9.76",taken_on:"2022-11-17T11:07:00.000-06:00",vital_type:{id:6,name:"glucose",units:"mg/dL",created_at:"2022-11-17T13:52:00.360-06:00",updated_at:"2022-11-17T13:52:00.360-06:00"},notes:null,source:"patient_device",id:1366,time_recorded:!0,severity:"critical_low",formatted_severity:"Critical - Low",vital_attributes:{},vital_status:"valid"},
];
const metadata = {
  glucose: { name: 'Glucose', units: 'mg/dL' },
  height: { name: 'Height', units: '"', longUnit: 'Inches' },
  weight: { name: 'Weight', units: 'lbs', longUnit: 'Pounds' },
  bmi: { name: 'BMI' },
  pulse: { name: 'Pulse' },
  temperature: { name: 'Temperature', units: 'F', longUnit: 'Fahrenheit' },
  respiration_rate: { name: 'Respiration Rate' },
  o2_saturation: { name: 'O2 Saturation', units: '%' },
};

const orderedVitalsArray = allVitals
  .sort((v1, v2) => moment(v2.taken_on).diff(moment(v1.taken_on)))
  .map(e => e?.vital_type?.name);

const orderedMetadata = orderedVitalsArray.reduce((current, item) => {
  current[item] = metadata[item];
  return current;
}, {});

console.log({
  orderedVitalsArray,
  orderedMetadata,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>

CodePudding user response:

Build a list of order, then loop through that, use the items as a key against the original list and rebuild the order.

const metadata = {
  glucose: {
    name: 'Glucose',
    units: 'mg/dL',
  },
  height: {
    name: 'Height',
    units: '"',
    longUnit: 'Inches',
  },
  weight: {
    name: 'Weight',
    units: 'lbs',
    longUnit: 'Pounds',
  },
  bmi: {
    name: 'BMI',
  },
  pulse: {
    name: 'Pulse',
  },
  temperature: {
    name: 'Temperature',
    units: 'F',
    longUnit: 'Fahrenheit',
  },
  respiration_rate: {
    name: 'Respiration Rate',
  },
  o2_saturation: {
    name: 'O2 Saturation',
    units: '%',
  },
}

const allVitals = 
[
    {
        "patient_id": 79,
        "vital_type_id": 4,
        "value": "171",
        "taken_on": "2022-11-17T13:19:00.000-06:00",
        "vital_type": {
            "id": 4,
            "name": "weight",
            "units": "pounds",
            "created_at": "2022-11-17T13:52:00.375-06:00",
            "updated_at": "2022-11-17T13:52:00.375-06:00"
        },
        "notes": null,
        "source": "patient_device",
        "id": 1399,
        "time_recorded": true,
        "severity": null,
        "formatted_severity": "-",
        "vital_attributes": {},
        "vital_status": "valid"
    },
    {
        "patient_id": 79,
        "vital_type_id": 6,
        "value": "9.76",
        "taken_on": "2022-11-17T11:07:00.000-06:00",
        "vital_type": {
            "id": 6,
            "name": "glucose",
            "units": "mg/dL",
            "created_at": "2022-11-17T13:52:00.360-06:00",
            "updated_at": "2022-11-17T13:52:00.360-06:00"
        },
        "notes": null,
        "source": "patient_device",
        "id": 1366,
        "time_recorded": true,
        "severity": "critical_low",
        "formatted_severity": "Critical - Low",
        "vital_attributes": {},
        "vital_status": "valid"
    }
];

const conditionedVitals = allVitals.sort( (a, b) => {
  // get ref to a and b taken_on values
  var a_taken_on = a['taken_on'];
  //console.log( Date.parse(a_taken_on) );
  var b_taken_on = b['taken_on'];
  //console.log( Date.parse(b_taken_on) );
  if ( a_taken_on > b_taken_on ) return 1;
  if ( b_taken_on > a_taken_on ) return -1;
  return 0;
});
//we have the original list, now we need to re-sort, by conditionedVitals list
// get the order of the list, by extracting the order of the conditionedVitals:
const orderList = conditionedVitals.map( (e) => { 
console.log(e.vital_type.name);
return e.vital_type.name } ) ;

console.log(orderList);
// we have our sort order now, we can do things:
const newList = orderList.map( (e) => {
 return metadata[e];
});
// new List prints out in the order which you require, but does not include items which were not included in the allVitals list!
console.log(newList);

CodePudding user response:

Do you really want this?

Before we look at how we might solve this, I would like to suggest that your desired format is problematic. An object is an inherently unordered container. Treating it as ordered is not only logically suspect, it can lead to real errors. For most cases, when you need an ordered result, an array is cleaner. Moreover, an array will allow you to have duplicates (multiple pulse readings, for instance.) You can't get that with an object. So I would suggest a much more useful and logical output format would look like this:

[
  {name: 'Glucose', units: 'mg/dL',},
  {name: 'Height', units: '"', longUnit: 'Inches',},
  {name: 'Weight', units: 'lbs', longUnit: 'Pounds',},
  {name: 'BMI',},
  {name: 'Pulse',},
  {name: 'Temperature', units: 'F', longUnit: 'Fahrenheit',},
  {name: 'Respiration Rate',},
  {name: 'O2 Saturation', units: '%',},
]

We can touch on how to do this after discussing how to get your requested result:

One Solution

I would build this on three utility functions, like this:

const uniq = (xs) => [... new Set (xs)]

const descend = (fn) => 
  (a, b, x = fn (a), y = fn (b)) => x < y ? 1 : x > y ? -1 : 0

const sortByFixedKeys = (xs, order) => [...xs] .sort (
  (a, b, x = order .indexOf (a), y = order .indexOf (b)) => 
    x == -1 ? y == -1 ? 0 : 1 : y == -1 ? -1 : x < y ? -1 : x > y ? 1 : 0
)

const createMetadata = (vitals, types) => 
  Object .fromEntries (
    sortByFixedKeys (
      Object .keys (types), 
      uniq ([... vitals] .sort (descend (x => x .taken_on)) .map (x => x .vital_type .name))
    ) .map (key => [key, types [key]])
  )


const allVitals = [{patient_id: 79, vital_type_id: 6, value: "9.76", taken_on: "2022-11-17T11: 07: 00.000-06: 00", vital_type: {id: 6, name: "glucose", units: "mg/dL", created_at: "2022-11-17T13: 52: 00.360-06: 00", updated_at: "2022-11-17T13: 52: 00.360-06: 00"}, notes: null, source: "patient_device", id: 1366, time_recorded: true, severity: "critical_low", formatted_severity: "Critical - Low", vital_attributes: {}, vital_status: "valid"}, {patient_id: 79, vital_type_id: 4, value: "171", taken_on: "2022-11-17T13: 19: 00.000-06: 00", vital_type: {id: 4, name: "weight", units: "pounds", created_at: "2022-11-17T13: 52: 00.375-06: 00", updated_at: "2022-11-17T13: 52: 00.375-06: 00"}, notes: null, source: "patient_device", id: 1399, time_recorded: true, severity: null, formatted_severity: "-", vital_attributes: {}, vital_status: "valid"}]
const types = {glucose: {name: "Glucose", units: "mg/dL"}, height: {name: "Height", units: '"', longUnit: "Inches"}, weight: {name: "Weight", units: "lbs", longUnit: "Pounds"}, bmi: {name: "BMI"}, pulse: {name: "Pulse"}, temperature: {name: "Temperature", units: "F", longUnit: "Fahrenheit"}, respiration_rate: {name: "Respiration Rate"}, o2_saturation: {name: "O2 Saturation", units: "%"}}

console .log (createMetadata (allVitals, types))
.as-console-wrapper {max-height: 100% !important; top: 0}

uniq is trivial, just returning an array with the duplicates removed.

descend turns a function that extracts an ordered value (string, number, date, {valueOf}) from an object into a comparator that can be passed to sort. That comparator will sort them in a descending order.

sortByFixedKeys is slightly more complex. It takes an array to sort and an array of those elements with a known ordering. It then sorts your array by the order the elements appear in the ordering. If one is not found, it goes to the end, in the same order it was in the original array. For example,

sortByFixedKeys ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [8, 6, 7, 5, 3, 0, 9])
//=> [8, 6, 7, 5, 3, 9, 1, 2, 4, 10, 11, 12]

Using these together in createMetadata, we pass to sortByFixedKeys the keys of our types, and an ordering array found by taking the unique values among the vital_type.name properties of our objects, once sorted descending by date. We turn these results back into an object by mapping the resulting keys into [key, types [key]] pairs, and reconstructing with createMetadata.

Since each helper is used in only one place in the main function, we could inline them. But these are genuinely useful on their own, especially the first two, so it makes sense to keep them separate. Besides, inlining them would lead to an unholy mess!

Alternative

To create the format I suggested, it's only a minor tweak, a simplification in fact:

const createMetadata = (vitals, types) => 
  sortByFixedKeys (
    Object .keys (types), 
    uniq ([... vitals] .sort (descend (x => x .taken_on)) .map (x => x .vital_type .name))
  ) .map (x => types [x])

This does much the same process, but instead of mapping the results to create entry objects and then calling Object .fromEntries, we simply map them to the associated types. Now we can have duplicated entries in this list, if the patient has multiple pulse readings.

  • Related