Jan F.
Jan F.

Reputation: 13

Setter function to dynamically access fields within an array which is a property

I have a javascript module representing a physical model and its data is provided in a data object. This module exports a setter function to set properties of the data object (Note: export removed in example below)

This works very well on the top hierarchical level but how to set specific values inside properties, e.g. a specific field of an array property?

var modeldata = {
  current: 100,
  pipeline: [150,200,210,220]
}

set = (variable, val) => {
  if (modeldata[variable] == undefined) {
    console.log('modeldata['+variable+'] is not defined');
  } else {
    if (modeldata[variable] == val) {
      console.log('no change necessary');
    } else {
      var old = modeldata[variable];
      modeldata[variable] = val;
      console.log('changing modeldata['+variable+']: ' + old + ' to ' + modeldata[variable])
    }
  }
}

set('curren', 100);   // modeldata[curren] is not defined, OK
set('current', 100);  // no change necessary, OK
set('current', 120);  // changing modeldata[current]: 100 to 120, OK
set('pipeline[0]', 42); // modeldata[pipeline[0]] is not defined, FAIL

How to make the set function issue modeldata['pipeline'][0] = 42; instead but still allow to set the current property as it works now?

I could simply create a specific setter for each property but I have > 100 properties and the amount of properties is not fixed.

Also, I could call set('pipeline', ...) and simply overwrite the whole array including the new data. But as stated above, I want to change only a specific field of the array without knowing or touching everything else.

Update: I'm looking for a solution where modeldata is only accessed within the set-function and there is no get-function available. Thus, set is my only interface to modeldata.

Upvotes: 1

Views: 91

Answers (5)

Nina Scholz
Nina Scholz

Reputation: 386644

Just walk the path.

var modeldata = {
    current: 100,
    pipeline: [150, 200, 210, 220]
}

function set(variable, val) {
    var path = variable.replace(/\[(\d+)\]/g, '.$1').split('.'),
        target = path.pop(),
        reference = path.reduce(function (r, k) { return r[k]; }, modeldata);

    if (target in reference) {
        if (reference[target] === val) {
            alert(variable + ' = ' + val + '\nno change necessary');
        } else {
            reference[target] = val;
        }
    } else {
        alert(variable + ' is not defined');
    }
}

set('curren', 100); // modeldata[curren] is not defined, OK
set('current', 100); // no change necessary, OK
set('current', 120); // changing modeldata[current]: 100 to 120, OK
set('pipeline[0]', 42); // changing modeldata[pipeline.0]: 150 to 42, OK
document.write('<pre>' + JSON.stringify(modeldata, 0, 4) + '</pre>');

Upvotes: 0

Federico Baron
Federico Baron

Reputation: 997

This is an edit to the set function in order to allow arrays and sub-properties changes:

set = (variable, val) => variable.split('.').reduce((prev, curr, i, arr) => {
  if (i === arr.length - 1) {
    if (prev[curr] == undefined) {
      console.log('modeldata[' + variable + '] is not defined');
    } else {
      if (prev[curr] == val) {
        console.log('no change necessary');
      } else {
        var old = prev[curr];
        prev[curr] = val;
        console.log('changing modeldata[' + variable + ']: ' + old + ' to ' + prev[curr]);
        return prev[curr];
      }
    }
  } else {
    return prev[curr];
  }
}, modeldata)

You will have

set('curren', 100); // modeldata[curren] is not defined, OK
set('current', 100); // no change necessary, OK
set('current', 120); // changing modeldata[current]: 100 to 120, OK
set('pipeline.0', 42); // changing modeldata[pipeline.0]: 150 to 42, OK

Upvotes: 1

RomanPerekhrest
RomanPerekhrest

Reputation: 92854

This condition if (modeldata[variable] .... checks for existance of distinct named property in modeldata object.
Extend you set function so it could consider array keys:

var modeldata = {
        current: 100,
        pipeline: [150,200,210,220]
    }

    var set = (variable, val, array_key) => {
        var prop = (typeof array_key == "number")? modeldata[variable][array_key] : modeldata[variable];
        if (prop == undefined) {
            console.log('modeldata['+variable+'] is not defined');
        } else {
            if (prop == val) {
                console.log('no change necessary');
            } else {
                var old = prop;
                if (typeof array_key == "number"){
                    modeldata[variable][array_key] = val;
                } else {
                    modeldata[variable] = val;
                }

                console.log('changing modeldata['+variable+']: ' + old + ' to ' + modeldata[variable])
            }
        }
    };

    set('curren', 100);   // modeldata[curren] is not defined, OK
    set('current', 100);  // no change necessary, OK
    set('current', 120);  // changing modeldata[current]: 100 to 120, OK
    set('pipeline', 42, 0); // m

    console.log(modeldata);
    // the output:
    Object {current: 120, pipeline: Array[4]} .....
    current: 120
    pipeline: Array[4]
             0: 42
             1: 200
             2: 210
             3: 220
             length: 4

Upvotes: 0

rishikesh tadaka
rishikesh tadaka

Reputation: 483

You can refer below code:

    var getBothIndexVariables = function (input) {
        var openingIndex = input.indexOf('[');
        var secondVal = "";
        var indexVariable;
        if (openingIndex != -1) {
            secondVal = input.substring(openingIndex + 1, input.length - 1);

            indexVariable = {
                firstIndexVariable: input.substring(0,openingIndex),
                secondIndexVariable: secondVal,
            }
        }
        return indexVariable;
    }
    var modeldata = {
        current: 100,
        pipeline: [150, 200, 210, 220]
    }

    set = (variable, val) => {
        var tempVariable = getBothIndexVariables(variable);
        if (tempVariable) {
            debugger;
            var value = modeldata[tempVariable.firstIndexVariable][tempVariable.secondIndexVariable];
            modeldata[tempVariable.firstIndexVariable][tempVariable.secondIndexVariable] = val;
            console.log(value);
            return;
        }
        if (modeldata[variable] == undefined) {
            console.log('modeldata[' + variable + '] is not defined');
        } else {
            if (modeldata[variable] == val) {
                console.log('no change necessary');
            } else {
                var old = modeldata[variable];
                modeldata[variable] = val;
                console.log('changing modeldata[' + variable + ']: ' + old + ' to ' + modeldata[variable])
            }
        }
    }

    set('curren', 100);   // modeldata[curren] is not defined, OK
    set('current', 100);  // no change necessary, OK
    set('current', 120);  // changing modeldata[current]: 100 to 120, OK
    set('pipeline[0]', 42); // modeldata[pipeline[0]] is not defined, FAIL

Upvotes: 0

Jan F.
Jan F.

Reputation: 13

I have created a quick and dirty workaround by adding this code at the very beginning of my set-function:

set = (variable, val) => {
  if (variable.indexOf('[')>0 && variable.indexOf(']')>0) {
    var old = eval('modeldata.'+variable);
    eval('modeldata.'+variable+'='+val);
    console.log('changing modeldata.'+variable+': ' + old + ' to ' + val)
    return;
  }

It works but it doesn't make me feel good to use eval(). I'd love to see better answers.

Upvotes: 0

Related Questions