Changing selects based on one select

Hi, everyone.

I’m trying to use a select to chose a preset. Each preset changes different select’s, which can then be modified.

I’ve set up a mockup notebook to be tinkered with:

1 Like

You can combine each set’s values into a unique key and then set your select’s value by that:

viewof s = Inputs.select(new Map([
  ["", "custom"],
  ["A1, B2, C3", "S1"],
  ["A2, B1, C2", "S2"],
  ["A1, B3, C1", "S3"],
  ["A3, B3, C2", "S4"],
]), {
  label: "Set",
  format: d => d[1],
  key: [a, b, c].join(", "),
})

If no key matches the select defaults to the first option.

1 Like

I was thinking the opposite: changing S moves A, B and C.

I think your code picks S according to the combination of A, B and C (which is nice, but not really necessary for what I have in mind).

I guess I was to focused on this quote in your notebook:

Ideally, if I manually change A, B or C, S changes to the equivalent S if it exists. If it doesn’t, to custom.

But I think I understand better now what you’re trying to do. These are intended to be presets, correct? Picking the right approach depends on how you intend to reference either of these inputs throughout the rest of your notebook. Can you share a bit about your use case?

Now that you mentioned it, I really stressed the less important thing.

Anyway, here’s the elevator pitch:

I’m an intensive care physician who works in different ICUs. Each hospital ICU has their own set of standard dilutions for the drugs that are administered to patients by IV pumps (in mL/h). Calculating the dose (in µg/min or mg/h) is something I have to do everyday to all the patients under my care – that time really adds up and is prone to miscalculations.

So I’ve built this:

No colorful visualization, but it does the work. The range sliders’ max value is the maximum dose, which helps me (1) not to overshoot the max dose and (2) to have an idea of how much of the present dose is, compared to the max, just by glancing.

The problem with the approach I’m taking now is that, when I choose a preset (Local), all the drug dilutions are set in stone — I cannot change a single one for a special case.

Sometimes a patient needs so much of a drug that we concentrate it. Like noradrenalin (80µg/mL). I want to change it to 160µg/mL (another option).

You can see what I have in mind in the Part II of my mockup:

I just wrote a comment… I forgot to “Reply” to you. There you go. :wink:

The first two pieces I think you’ll need are:

  1. setting a select’s value based on another value. Here is a simplified example, but the value can be anything:
    viewof choice1 = Inputs.select(["", "a", "b", "c", "d"])
    
    viewof choice2 = Inputs.select(new Map([
      ["", [0, 1]], // default option
      ["b", [0, .75]],
      ["c", [0, .5]],
    ]), {key: choice1})
    
  2. Not resetting the current value when a dependency changes. A cell receives its previous value via this, allowing us to keep the previous slider value even if e.g. the range changes:
    viewof range1 = Inputs.range(choice2, {value: this?.value ?? 0})
    

Next I would recommend to take a more data driven approach. Define all drugs in a single cell, with some default min/max ranges. Then, in another cell, define the ranges/dilutions that should be overridden. Lastly, you can generate your inputs “on the fly” and combine them via Inputs.form().

Here is an addition to your original notebook to show the general idea (it’s only one step in the direction though):

function createDrugsWidget(choices) {
  const createInput = (name, {
    label = name,
    unit = drugs[hosp][name].u,
    formula = (c, max) => max * weight / c,    
  }) => {
    const {c, max} = drugs[hosp][name];
    const input = Inputs.range([0, formula(c, max)], {
      label: htl.html`<div style="text-align:right">${label}<br><span style="white-space:nowrap">${c} ${unit}`,
      width: "100%",
      step: .1,
      value: 0,
    });
    input.style.cssText += "--label-width:90px";
    return [name, input];
  };

  const themeInput = (name, input) => {
    const {min, max, u} = drugs[hosp][name];
    return htl.html`<div>
      <div style="padding: 0 8px">${input}</div>
      <div style="
        font: 11px var(--sans-serif);
        padding: 4px 0;
        text-align: center;
        border: 1px solid #eee;
        border-width: 1px 0;
        color: #444;
      ">${min}&#8239;–&#8239;${max}&#8239;${u}`;
  };

  return Inputs.form(
    Object.fromEntries(Object.entries(choices).map(d => createInput(...d))),
    {
      template: inputs => htl.html`<div style="
        display:grid;
        grid-template-columns: repeat(auto-fill, minmax(300px, 3fr));
      ">${
        Object.entries(inputs).map(d => themeInput(...d))
      }`
    }
  );
}
viewof sedativos = createDrugsWidget({
  mida: {label: "Midazolam"},
  fenta: {label: "Fentanil"},
  pcdex: {label: "Precedex"},
  ppfol: {label: "Propofol"},
  ket: {label: "Ketamina", unit: "mg/mL", formula: (c, max) => max * weight / c * 60 / 1000},
})
viewof dvas = createDrugsWidget({
  nora: {label: "Noradrenalina"},
  vaso: {label: "Vasopressina"},
  dobuta: {label: "Dobutamina"},
  nip: {label: "Nitroprusseto"},
  trid: {label: "Nitroglicerina"},
  dopa: {label: "Dopamina"},
  mil: {label: "Milrinona"},
})