0

I'm writing a tiny reactive framework where I need to find out which subscriber needs updating. I'm implementing deep binding and I'm running into a wall how to find subscribers in an effective manner.

A stored variable can be an object, so for example

{
    "user": {
        "preferences": {
            "food": "vegetarian"
        }
    }
}

You can get content to any level of this variable like this

getVar("user_preferences_food");
getVar("user_preferences");

However, you can also update it like that

setVar("user_preferences_food", "meat");
setVar("user_preferences", {"food": "meat"});

But in case of the first setVar (user_preferences_food) how can I find the subscriber using getVar("user_preferences"); or even getVar("user"); most effectively.

I already got it working by splitting the var on _ and then one by one concatting the next level and merging all the resulting arrays. But this is very resource intensive. Especially if there are a lot of subscribers. There must be a better way to find them that is less resource intensive.

Edit: I left out part of the explanation.

There is a subscribe method too

subscribe("user", cb);
subscribe("user_preferences", cb);
subscribe("user_preferences_food", cb);

These subscriptions are stored in an array in the framework.

As soon as "user_preferences_food" is updated for example, all subscriptions above should be triggered. But obviously not subscribe('othervar');

simplification of the subscribe method:

var subscriptions = [];
function subscribe(var, callback){
   subscriptions.push({var: var, cb: callback});
}

Simplification of getVar

vars = {};
getVar(var){
   // find var in vars with this logic: https://stackoverflow.com/a/18937118/249710

      // current exact match on subscribers, but need the "parents, grandparents etc here
       var toUpdate = _.where(subscriptions, {
        "var" : var
    });
    _.each(toUpdate, function(sub){ sub.cb();});
}

Storing or getting data as part of the key I've already got covered. It is just finding the subscribers in the most effective manner

ps: this is in an environment where I cannot rely on ES6 yet (not all users have it enabled), there is no DOM but I do have underscore included. (Titanium app development platform)

Rene Pot
  • 24,681
  • 7
  • 68
  • 92
  • please add what you have tried. – Nina Scholz Jul 11 '18 at 10:02
  • @NinaScholz the concatting thing... but it is too heavy and not a solution. – Rene Pot Jul 11 '18 at 10:05
  • Have a look here @RenePot : [`Accessing nested JavaScript objects with string key`](https://stackoverflow.com/questions/6491463/accessing-nested-javascript-objects-with-string-key) – Alex Jul 11 '18 at 10:08
  • Is this supposed to be purely client-side, or what? Can’t really imagine any situation where managing a large list of subscribers client-side only could make much sense to begin with … is there no backend with a proper database that could handle this? – CBroe Jul 11 '18 at 10:09
  • @CBroe it is frontend, variables stored are only in-memory and for UI purpose only. – Rene Pot Jul 11 '18 at 10:15
  • please add what subscibe is supposed to do. what is `cb`? if *callback*, please add an example with `cb` and result of the function call. – Nina Scholz Jul 11 '18 at 10:30
  • @NinaScholz added it! – Rene Pot Jul 11 '18 at 10:35

3 Answers3

1

I would try to make a list for the callbacks, so you loop trough one list so you dont have to search, because you know the list is there with all the callbacks.

So if you call setVar('user_prefs') you set a seperate list with the root var. in this case its the user.

if any object is changed with setVar (in depth or not) you go to you're root var, get the list and loop trough this list with the callbacks.

The beauty of this is you can set a list with the root var, var cbList[FIRSTVAR] this contains all the callbacks. No searching just loop.

Its the mongoDb principle, the data is ready to go, you don't search because you know the list is already there.

Mad
  • 71
  • 1
  • 8
0

You could split the string and use it for reduceing the object.

function getVar(object, path) {
    return path
        .split('_')
        .reduce(function (o, k) {
            return (o || {})[k];
        }, object);
}

function setVar(object, path, value) {
    var keys = path.split('_'),
        last = keys.pop();

    keys.reduce(function (o, k) {
        return o[k] = o[k] || {};
    }, object)[last] = value;
}


var object = { user: { preferences: { food: "vegetarian" } } };

console.log(getVar(object, "user_preferences_food"));
console.log(getVar(object, "user_preferences"));

setVar(object, "user_preferences_food", "meat");
console.log(object);

setVar(object, "user_preferences", {"food": "meat"});
console.log(object);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • I'm not looking for the data (got that covered already) but the subscribers using the keys. So... any subscriber on "user" and "user_preferences" should get updated when "user_preferences_food" is changed. How do I find those subscribers when I do that? I'll update the question too – Rene Pot Jul 11 '18 at 10:21
0

I ended up doing this:

var options = [];
var parts = key.split('_');
var string = parts[0];
_.each(parts, function(p, i){
    if (i > 0) string += '_' + p;
    options.push(string);
});

var toUpdate = _.filter(subscribers, function(sub){
    if (sub.var.indexOf(key + '_') === 0) return true;
    if (options.indexOf(sub.var) > -1) return true;
    return false;
});

So checking with indexOf on the string to see if there are children. And building an array with parents so any layer is a match, and doing an indexOf on that as well. I think this is the least complicated method of implementing it

Rene Pot
  • 24,681
  • 7
  • 68
  • 92