0

I'd like to pass in variables defined by the user when using pouchdb's plugin, upsert. Ideally, something like this:

var res = db.upsert(uniqueID, (doc) => {
 doc.data.var1.var2.var3 = userInput
}

What I've Tried:

var res = db.upsert(uniqueID, (doc) => {
 doc.data = {[var1]:
                {[var2]:
                  {[var3]: userInput}
                 }
             }
}

This results in the contents of doc.data being overwritten repeatedly with each new variable.

Now, is there a way to simply define a nested, variable-dependent key that I intend to upsert?

A document for every var3: userInput seems way too excessive atm.

mak49
  • 15
  • 4
  • The assignment to `doc.data` using variable keys works, so there's something missing. Please clarify what you mean by "being overwritten repeatedly with each new variable". – RamblinRose Mar 29 '21 at 11:09
  • using variables from the right side of the ```=``` works well. What I want to do is to pass them on the left side like ```doc.data.var1.var2 = var3```. In effect, ```doc.data``` should have multiple arrays, defined by multiple variables over time. The last example above always returns ```doc.data``` as the most recent ```upsert```, having overwritten any other(as is intended). – mak49 Mar 29 '21 at 14:09
  • You say "multiple arrays" but I see no arrays at all, just nested objects. Generally, is it that you need a way to add a nested value following some key path, e.g. keys = [var1,var2,var3], value = someValue ? – RamblinRose Mar 29 '21 at 14:17

1 Answers1

1

There exists no "if a key doesn't exist make its value an object" behavior in Javascript. For example if doc = {} then

doc.data.foo.bar = "fail"

will always fail because the data is undefined.

Instead a simple function may be defined to provide such functionality.

function setDeepValue(target, keyPath, value) {
  const key = keyPath.shift();
  if (key !== undefined) {
    // there is more work to be done.
    if (keyPath.length === 0) {
      // we're at the end of the path, assign key to value
      target[key] = value;
    } else {
      if (target[key] === undefined) {
        // create an object for the key
        target[key] = {};
      }
      // keep going.
      setDeepValue(target[key], keyPath, value);
    }
  }
}
//
// demo the function
//
const doc = {};
let var1 = "key1",
  var2 = "key2",
  var3 = "key3";
const keyPath = ["data", var1, var2, var3];
const value = "My Value";
console.log("doc before:\n" + JSON.stringify(doc, undefined, 3));
setDeepValue(doc, keyPath, value);
console.log("doc after:\n" + JSON.stringify(doc, undefined, 3));

I don't claim the above code is optimal, but it does the job.

RamblinRose
  • 4,883
  • 2
  • 21
  • 33
  • 1
    Amazing! This works flawlessly. Thank you so much for your time and effort. – mak49 Mar 29 '21 at 17:57
  • 1
    Tried to optimize it with ```newDoc = {...oldDoc, ...newArray}``` but it suffers the same fate as upsert. Maybe years from now, when JS has evolved, we'll all look back at this and have a laugh. – mak49 Mar 29 '21 at 18:40
  • 1
    Indeed `newDoc = {...obj1,...jb2}` works, but only for ES2018. With ES2015 `Object.assign(obj1, obj2)` works as well. However, neither work in your case as it your preference is to sidestep explicit object creation as remarked in the "What I've Tried" portion of the post. Glad to have helped. re: https://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically – RamblinRose Mar 30 '21 at 15:04