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.