Shorthand to manipulate an object attribute and reassign

Viewed 64

I have a deeply nested object and I want to manipulate a value of it and reassign it again. Is there a shorthand way for this other than writing it all out again or assigning it to a variable:

createStops[idx]['place']['create'][stop][key][value] = createStops[idx]['place']['create'][stop][key][value].toString()

looks ugly doesn't it? Something like:

createStops[idx]['place']['create'][stop][key][value].toStringAndReassign()

but JS built in.

Edit: In my case it is a number, if it's for your case too please check out @MarkMeyer answer.

6 Answers

No, there isn't.

Assigning a new value requires an assignment.

Strings are immutable, so you can't convert an existing value into a string in-place.

Given a value that's a number, if you just want it to be a string, you can coerce to a string with an assignment operator:

let o = {
    akey: {
        akey:{
            value: 15
        }
    }
}

o.akey.akey.value += ''
console.log(o)

No, Going to the same index is needed to store the value

Although it is not possible as mentioned by @Quentin you can define a custom getter in your object like:

var foo = {
  a: 5,
  b: 6,
  get c () {
    return this.b.toString()+' text'
  }
};

console.log(foo.c);

You're not reassigning the value as you are semantically formatting your values. In order to format your value you are mutating your initial object. If you do not pretend to modify an object for formatting purposes that will work just fine.

You do not have integrated functions to use like that, but you could use of some utilitary functions of your own to help you manage assignements and make it less verbal.

SPOIL : The final use look like

// call the function to do +1 at the specified key
executeFunctionAtKey(
  // The object to change the value on
  createStops,

  // The path
  `${idx}.place.create.${stop}.${key}.${value}`,

  // The thing to do
  (x) => x + 1,
);

const createStops = {
  idx: {
    place: {
      create: {
        stop: {
          key: {
            value: 5,
          },
        },
      },
    },
  },
};

const idx = 'idx';
const stop = 'stop';
const key = 'key';
const value = 'value';

// Function that go to the specified key and
// execute a function on it.
// The new value is the result of the func
// You can do your toString there, or anything else
function executeFunctionAtKey(obj, path, func) {
  const keys = path.split('.');

  if (keys.length === 1) {
    obj[path] = func(obj[key]);

    return obj;
  }

  const lastPtr = keys.slice(0, keys.length - 1).reduce((tmp, x) => tmp[x], obj);

  lastPtr[keys[keys.length - 1]] = func(lastPtr[keys[keys.length - 1]]);

  return obj;
}

// call the function to do +1 at the specified key
executeFunctionAtKey(
  // The object to change the value on
  createStops,

  // The path
  `${idx}.place.create.${stop}.${key}.${value}`,

  // The thing to do
  (x) => x + 1,
);

console.log(createStops);



with the toString example from Number to String

const createStops = {
  idx: {
    place: {
      create: {
        stop: {
          key: {
            value: 5,
          },
        },
      },
    },
  },
};

const idx = 'idx';
const stop = 'stop';
const key = 'key';
const value = 'value';

// Function that go to the specified key and
// execute a function on it.
// The new value is the result of the func
// You can do your toString there, or anything else
function executeFunctionAtKey(obj, path, func) {
  const keys = path.split('.');

  if (keys.length === 1) {
    obj[path] = func(obj[key]);

    return obj;
  }

  const lastPtr = keys.slice(0, keys.length - 1).reduce((tmp, x) => tmp[x], obj);

  lastPtr[keys[keys.length - 1]] = func(lastPtr[keys[keys.length - 1]]);

  return obj;
}

// call the function to do +1 at the specified key
executeFunctionAtKey(
  // The object to change the value on
  createStops,

  // The path
  `${idx}.place.create.${stop}.${key}.${value}`,

  // The thing to do
  (x) => x.toString(),
);

console.log(createStops);

Theoretically you could build a function that takes an object, a path and the property to set it to.

This will reduce the readability of your code, so i would advice using ordinary assignment. But if you need it check out the snippet below:

//
function setProp(object, path, val) {
    var parts = path.split("/").filter(function (p) { return p.length > 0; });
    var pathIndex = 0;
    var currentTarget = object;
    while (pathIndex < parts.length - 1) {
        currentTarget = currentTarget[parts[pathIndex]];
        pathIndex++;
    }
    if (val instanceof Function) {
        currentTarget[parts[pathIndex]] = val(currentTarget[parts[pathIndex]]);
    }
    else {
        currentTarget[parts[pathIndex]] = val;
    }
    return object;
}
var createStops = {
    idx: {
        place: {
            create: {
                stop: {
                    key: {
                        value: 5
                    }
                }
            }
        }
    }
};
function toString(p) { return p.toString(); }
console.log(JSON.stringify(createStops, null, 4));
setProp(createStops, 'idx/place/create/stop/key/value', toString);
console.log(JSON.stringify(createStops, null, 4));

UPDATE 1

Allowed passing functions and used OP JSON structure for snippet

Related