Reaching into object from array in javascript? - javascript

How can I reach into a object using an array and set a value - preferably without using eval, doing something like object[eval(["key", "deepkey"].split("")) = "newvalue"?
Doing it manually, I would just do object.key.deepkey = "newvalue", but again, I need to do this using an array to reach into the right property.
The object for reference:
object = {
key: {
deepKey: "value"
}
}

You can use a recursive function to step through each level of the array (or object) like so:
function val(array, indices) {
if(indices.length > 1) {
var idx = indices.shift();
return val(array[idx], indices);
}
else {
return array[indices.shift()];
}
}
var obj = { a: { b: 'c' } };
//result is 'c'
var result = val(obj, ['a', 'b']);
If you want to get an object reference, simply specify the second arg only up to that:
var obj = {
a: {
b: {
c: 'foo'
}
}
};
var ref = val(obj, ['a', 'b']);
//ref is now obj.a.b, so you can do something like...
ref.x = 'bar';
console.dir(ref); //outputs something like { c: 'foo', x: 'bar' }

You can write array type syntax as. jsfiddle
object = {
key: {
deepKey: "value"
}
}
object['key']['deepkey']='newvalue'
if you have keys in array you can do this
var keys = ['key','deepkey'];
var obj = object;
for(var k =0; k <keys.length-1; k++){
obj= obj[keys[k]];
}
obj[keys[k]] = 'newvalue'

You can take the function from this question and rework it to access properties of an object.
http://jsfiddle.net/jbabey/Mu4rP/
var getPropByName = function (propName, context) {
var namespaces = propName.split('.');
for(var i = 0; i < namespaces.length; i++) {
context = context[namespaces[i]];
}
return context;
};
var myObject = {
someKey: {
deepKey: "value"
}
};
myObject.someKey.deepKey; // "value"
getPropByName('someKey.deepKey', myObject); "value"

An alternative could be using Array.map this way:
function deepkey(obj,keys,set){
var i=1
,kys = keys.split('.')
,exist = kys.map( function(k){
var prev = this[i-1], isobj = prev.constructor === Object;
this.push( isobj && k in prev ? prev[k] : prev);
return (i++,this[i-1]);},
[obj]
)
,x = exist[exist.length-2];
if (x && x.constructor === Object && set){
x[kys[kys.length-1]] = set;
}
return x[kys.pop()] || null;
}
// usage
var obj = { a:{ b:{ c:1, cc:{ d:{ e:{ a:1,b:2,c:3 } } } } } };
// assign [1,2,3,4,5] to obj.a.b.cc.d.e.b
console.log(deepkey(obj,'a.b.cc.d.e.b',[1,2,3,4,5])); //=> [1,2,3,4,5]
// get obj.a.b.cc.d.e.b[2]
console.log(deepkey(obj,'a.b.cc.d.e.b')[2]); //=> 3
// get non existing path obj.a.b.c.d.e.b
console.log(deepkey(obj,'a.b.c.d.e.b')); //=> null

Related

Js how to set an item at a nested index in an empty array [duplicate]

I hope someone can help me with this Javascript.
I have an Object called "Settings" and I would like to write a function that adds new settings to that object.
The new setting's name and value are provided as strings. The string giving the setting's name is then split by the underscores into an array. The new setting should get added to the existing "Settings" object by creating new nested objects with the names given by each part of the array, except the last part which should be a string giving the setting's value. I should then be able to refer to the setting and e.g. alert its value. I can do this in a static way like this...
var Settings = {};
var newSettingName = "Modules_Video_Plugin";
var newSettingValue = "JWPlayer";
var newSettingNameArray = newSettingName.split("_");
Settings[newSettingNameArray[0]] = {};
Settings[newSettingNameArray[0]][newSettingNameArray[1]] = {};
Settings[newSettingNameArray[0]][newSettingNameArray[1]][newSettingNameArray[2]] = newSettingValue;
alert(Settings.Modules.Mediaplayers.Video.Plugin);
... the part that creates the nested objects is doing this ...
Settings["Modules"] = {};
Settings["Modules"]["Video"] = {};
Settings["Modules"]["Video"]["Plugin"] = "JWPlayer";
However, as the number of parts that make up the setting name can vary, e.g. a newSettingName could be "Modules_Floorplan_Image_Src", I'd like to do this dynamically using a function such as...
createSetting (newSettingNameArray, newSettingValue);
function createSetting(setting, value) {
// code to create new setting goes here
}
Can anyone help me work out how to do this dynamically?
I presume there has to be a for...loop in there to itterate through the array, but I haven't been able to work out a way to create the nested objects.
If you've got this far thanks very much for taking the time to read even if you can't help.
Put in a function, short and fast (no recursion).
var createNestedObject = function( base, names ) {
for( var i = 0; i < names.length; i++ ) {
base = base[ names[i] ] = base[ names[i] ] || {};
}
};
// Usage:
createNestedObject( window, ["shapes", "triangle", "points"] );
// Now window.shapes.triangle.points is an empty object, ready to be used.
It skips already existing parts of the hierarchy. Useful if you are not sure whether the hierarchy was already created.
Or:
A fancier version where you can directly assign the value to the last object in the hierarchy, and you can chain function calls because it returns the last object.
// Function: createNestedObject( base, names[, value] )
// base: the object on which to create the hierarchy
// names: an array of strings contaning the names of the objects
// value (optional): if given, will be the last object in the hierarchy
// Returns: the last object in the hierarchy
var createNestedObject = function( base, names, value ) {
// If a value is given, remove the last name and keep it for later:
var lastName = arguments.length === 3 ? names.pop() : false;
// Walk the hierarchy, creating new objects where needed.
// If the lastName was removed, then the last object is not set yet:
for( var i = 0; i < names.length; i++ ) {
base = base[ names[i] ] = base[ names[i] ] || {};
}
// If a value was given, set it to the last name:
if( lastName ) base = base[ lastName ] = value;
// Return the last object in the hierarchy:
return base;
};
// Usages:
createNestedObject( window, ["shapes", "circle"] );
// Now window.shapes.circle is an empty object, ready to be used.
var obj = {}; // Works with any object other that window too
createNestedObject( obj, ["shapes", "rectangle", "width"], 300 );
// Now we have: obj.shapes.rectangle.width === 300
createNestedObject( obj, "shapes.rectangle.height".split('.'), 400 );
// Now we have: obj.shapes.rectangle.height === 400
Note: if your hierarchy needs to be built from values other that standard objects (ie. not {}), see also TimDog's answer below.
Edit: uses regular loops instead of for...in loops. It's safer in cases where a library modifies the Array prototype.
function assign(obj, keyPath, value) {
lastKeyIndex = keyPath.length-1;
for (var i = 0; i < lastKeyIndex; ++ i) {
key = keyPath[i];
if (!(key in obj)){
obj[key] = {}
}
obj = obj[key];
}
obj[keyPath[lastKeyIndex]] = value;
}
Usage:
var settings = {};
assign(settings, ['Modules', 'Video', 'Plugin'], 'JWPlayer');
My ES2015 solution. Keeps existing values.
const set = (obj, path, val) => {
const keys = path.split('.');
const lastKey = keys.pop();
const lastObj = keys.reduce((obj, key) =>
obj[key] = obj[key] || {},
obj);
lastObj[lastKey] = val;
};
Example:
const obj = {'a': {'prop': {'that': 'exists'}}};
set(obj, 'a.very.deep.prop', 'value');
console.log(JSON.stringify(obj));
// {"a":{"prop":{"that":"exists"},"very":{"deep":{"prop":"value"}}}}
Using ES6 is shorten. Set your path into an array.
first, you have to reverse the array, to start filling the object.
let obj = ['a','b','c'] // {a:{b:{c:{}}}
obj.reverse();
const nestedObject = obj.reduce((prev, current) => (
{[current]:{...prev}}
), {});
Another recursive solution:
var nest = function(obj, keys, v) {
if (keys.length === 1) {
obj[keys[0]] = v;
} else {
var key = keys.shift();
obj[key] = nest(typeof obj[key] === 'undefined' ? {} : obj[key], keys, v);
}
return obj;
};
Example usage:
var dog = {bark: {sound: 'bark!'}};
nest(dog, ['bark', 'loudness'], 66);
nest(dog, ['woff', 'sound'], 'woff!');
console.log(dog); // {bark: {loudness: 66, sound: "bark!"}, woff: {sound: "woff!"}}
I love this ES6 immutable way to set certain value on nested field:
const setValueToField = (fields, value) => {
const reducer = (acc, item, index, arr) => ({ [item]: index + 1 < arr.length ? acc : value });
return fields.reduceRight(reducer, {});
};
And then use it with creating your target object.
const targetObject = setValueToField(['one', 'two', 'three'], 'nice');
console.log(targetObject); // Output: { one: { two: { three: 'nice' } } }
Lodash has a _.set method to achieve this
let obj = {}
_.set(obj, ['a', 'b', 'c', 'd'], 'e')
or
_.set(obj, 'a.b.c.d', 'e')
// which generate the following object
{
"a": {
"b": {
"c": {
"d": "e"
}
}
}
}
Here is a simple tweak to jlgrall's answer that allows setting distinct values on each element in the nested hierarchy:
var createNestedObject = function( base, names, values ) {
for( var i in names ) base = base[ names[i] ] = base[ names[i] ] || (values[i] || {});
};
Hope it helps.
Here is a functional solution to dynamically create nested objects.
const nest = (path, obj) => {
const reversedPath = path.split('.').reverse();
const iter = ([head, ...tail], obj) => {
if (!head) {
return obj;
}
const newObj = {[head]: {...obj}};
return iter(tail, newObj);
}
return iter(reversedPath, obj);
}
Example:
const data = {prop: 'someData'};
const path = 'a.deep.path';
const result = nest(path, data);
console.log(JSON.stringify(result));
// {"a":{"deep":{"path":{"prop":"someData"}}}}
Inspired by ImmutableJS setIn method which will never mutate the original. This works with mixed array and object nested values.
function setIn(obj = {}, [prop, ...rest], value) {
const newObj = Array.isArray(obj) ? [...obj] : {...obj};
newObj[prop] = rest.length ? setIn(obj[prop], rest, value) : value;
return newObj;
}
var obj = {
a: {
b: {
c: [
{d: 5}
]
}
}
};
const newObj = setIn(obj, ["a", "b", "c", 0, "x"], "new");
//obj === {a: {b: {c: [{d: 5}]}}}
//newObj === {a: {b: {c: [{d: 5, x: "new"}]}}}
Appreciate that this question is mega old! But after coming across a need to do something like this in node, I made a module and published it to npm.
Nestob
var nestob = require('nestob');
//Create a new nestable object - instead of the standard js object ({})
var newNested = new nestob.Nestable();
//Set nested object properties without having to create the objects first!
newNested.setNested('biscuits.oblong.marmaduke', 'cheese');
newNested.setNested(['orange', 'tartan', 'pipedream'], { poppers: 'astray', numbers: [123,456,789]});
console.log(newNested, newNested.orange.tartan.pipedream);
//{ biscuits: { oblong: { marmaduke: 'cheese' } },
orange: { tartan: { pipedream: [Object] } } } { poppers: 'astray', numbers: [ 123, 456, 789 ] }
//Get nested object properties without having to worry about whether the objects exist
//Pass in a default value to be returned if desired
console.log(newNested.getNested('generic.yoghurt.asguard', 'autodrome'));
//autodrome
//You can also pass in an array containing the object keys
console.log(newNested.getNested(['chosp', 'umbridge', 'dollar'], 'symbols'));
//symbols
//You can also use nestob to modify objects not created using nestob
var normalObj = {};
nestob.setNested(normalObj, 'running.out.of', 'words');
console.log(normalObj);
//{ running: { out: { of: 'words' } } }
console.log(nestob.getNested(normalObj, 'random.things', 'indigo'));
//indigo
console.log(nestob.getNested(normalObj, 'improbable.apricots'));
//false
Inside your loop you can use lodash.set and will create the path for you:
...
const set = require('lodash.set');
const p = {};
const [type, lang, name] = f.split('.');
set(p, [lang, type, name], '');
console.log(p);
// { lang: { 'type': { 'name': '' }}}
try using recursive function:
function createSetting(setting, value, index) {
if (typeof index !== 'number') {
index = 0;
}
if (index+1 == setting.length ) {
settings[setting[index]] = value;
}
else {
settings[setting[index]] = {};
createSetting(setting, value, ++index);
}
}
I think, this is shorter:
Settings = {};
newSettingName = "Modules_Floorplan_Image_Src";
newSettingValue = "JWPlayer";
newSettingNameArray = newSettingName.split("_");
a = Settings;
for (var i = 0 in newSettingNameArray) {
var x = newSettingNameArray[i];
a[x] = i == newSettingNameArray.length-1 ? newSettingValue : {};
a = a[x];
}
I found #jlgrall's answer was great but after simplifying it, it didn't work in Chrome. Here's my fixed should anyone want a lite version:
var callback = 'fn.item1.item2.callbackfunction',
cb = callback.split('.'),
baseObj = window;
function createNestedObject(base, items){
$.each(items, function(i, v){
base = base[v] = (base[v] || {});
});
}
callbackFunction = createNestedObject(baseObj, cb);
console.log(callbackFunction);
I hope this is useful and relevant. Sorry, I've just smashed this example out...
You can define your own Object methods; also I'm using underscore for brevity:
var _ = require('underscore');
// a fast get method for object, by specifying an address with depth
Object.prototype.pick = function(addr) {
if (!_.isArray(addr)) return this[addr]; // if isn't array, just get normally
var tmpo = this;
while (i = addr.shift())
tmpo = tmpo[i];
return tmpo;
};
// a fast set method for object, put value at obj[addr]
Object.prototype.put = function(addr, val) {
if (!_.isArray(addr)) this[addr] = val; // if isn't array, just set normally
this.pick(_.initial(addr))[_.last(addr)] = val;
};
Sample usage:
var obj = {
'foo': {
'bar': 0 }}
obj.pick('foo'); // returns { bar: 0 }
obj.pick(['foo','bar']); // returns 0
obj.put(['foo', 'bar'], -1) // obj becomes {'foo': {'bar': -1}}
A snippet for those who need to create a nested objects with support of array keys to set a value to the end of path. Path is the string like: modal.product.action.review.2.write.survey.data. Based on jlgrall version.
var updateStateQuery = function(state, path, value) {
var names = path.split('.');
for (var i = 0, len = names.length; i < len; i++) {
if (i == (len - 1)) {
state = state[names[i]] = state[names[i]] || value;
}
else if (parseInt(names[i+1]) >= 0) {
state = state[names[i]] = state[names[i]] || [];
}
else {
state = state[names[i]] = state[names[i]] || {};
}
}
};
Set Nested Data:
function setNestedData(root, path, value) {
var paths = path.split('.');
var last_index = paths.length - 1;
paths.forEach(function(key, index) {
if (!(key in root)) root[key] = {};
if (index==last_index) root[key] = value;
root = root[key];
});
return root;
}
var obj = {'existing': 'value'};
setNestedData(obj, 'animal.fish.pet', 'derp');
setNestedData(obj, 'animal.cat.pet', 'musubi');
console.log(JSON.stringify(obj));
// {"existing":"value","animal":{"fish":{"pet":"derp"},"cat":{"pet":"musubi"}}}
Get Nested Data:
function getNestedData(obj, path) {
var index = function(obj, i) { return obj && obj[i]; };
return path.split('.').reduce(index, obj);
}
getNestedData(obj, 'animal.cat.pet')
// "musubi"
getNestedData(obj, 'animal.dog.pet')
// undefined
Try this: https://github.com/silkyland/object-to-formdata
var obj2fd = require('obj2fd/es5').default
var fd = obj2fd({
a:1,
b:[
{c: 3},
{d: 4}
]
})
Result :
fd = [
a => 1,
b => [
c => 3,
d => 4
]
]
Here is a decomposition to several useful functions, that each preserve existing data. Does not handle arrays.
setDeep: Answers question. Non-destructive to other data in the object.
setDefaultDeep: Same, but only sets if not already set.
setDefault: Sets a key if not already set. Same as Python's setdefault.
setStructure: Helper function that builds the path.
// Create a nested structure of objects along path within obj. Only overwrites the final value.
let setDeep = (obj, path, value) =>
setStructure(obj, path.slice(0, -1))[path[path.length - 1]] = value
// Create a nested structure of objects along path within obj. Does not overwrite any value.
let setDefaultDeep = (obj, path, value) =>
setDefault(setStructure(obj, path.slice(0, -1)), path[path.length - 1], value)
// Set obj[key] to value if key is not in object, and return obj[key]
let setDefault = (obj, key, value) =>
obj[key] = key in obj ? obj[key] : value;
// Create a nested structure of objects along path within obj. Does not overwrite any value.
let setStructure = (obj, path) =>
path.reduce((obj, segment) => setDefault(obj, segment, {}), obj);
// EXAMPLES
let temp = {};
// returns the set value, similar to assignment
console.log('temp.a.b.c.d:',
setDeep(temp, ['a', 'b', 'c', 'd'], 'one'))
// not destructive to 'one'
setDeep(temp, ['a', 'b', 'z'], 'two')
// does not overwrite, returns previously set value
console.log('temp.a.b.z: ',
setDefaultDeep(temp, ['a', 'b', 'z'], 'unused'))
// creates new, returns current value
console.log('temp["a.1"]: ',
setDefault(temp, 'a.1', 'three'))
// can also be used as a getter
console.log("temp.x.y.z: ",
setStructure(temp, ['x', 'y', 'z']))
console.log("final object:", temp)
I'm not sure why anyone would want string paths:
They are ambiguous for keys with periods
You have to build the strings in the first place
Since I started with something from this page, I wanted to contribute back
Other examples overwrote the final node even if it was set, and that wasn't what I wanted.
Also, if returnObj is set to true, it returns the base object. By default, falsy, it returns the deepest node.
function param(obj, path, value, returnObj) {
if (typeof path == 'string') path = path.split(".");
var child = obj;
path.forEach((key, i) => {
if (!(key in child)) {
child[key] = (i < path.length-1) ? {} : value || {};
}
child = child[key];
});
return returnObj ? obj : child;
}
var x = {};
var xOut = param(x, "y.z", "setting")
console.log(xOut);
xOut = param(x, "y.z", "overwrite") // won't set
console.log(xOut);
xOut = param(x, "y.a", "setting2")
console.log(xOut);
xOut = param(x, "y.a", "setting2", true) // get object rather than deepest node.
console.log(xOut);
You can also do something where numeric keys are placed in arrays (if they don't already exist). Note that numeric keys won't convert to arrays for the first element of the path, since that's set by the type of your base-object.
function isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
function param(obj, path, value, returnObj) {
if (typeof path == 'string') path = path.split(".");
var child = obj;
path.forEach((key, i) => {
var nextKey = path[i+1];
if (!(key in child)) {
child[key] = (nextKey == undefined && value != undefined
? value
: isNumber(nextKey)
? []
: {});
}
child = child[key];
});
return returnObj ? obj : child;
}
var x = {};
var xOut = param(x, "y.z", "setting")
console.log(xOut);
xOut = param(x, "y.z", "overwrite") // won't set
console.log(xOut);
xOut = param(x, "y.a", "setting2")
console.log(xOut);
xOut = param(x, "y.a", "setting2", true) // get object rather than deepest node.
xOut = param(x, "1.0.2.a", "setting")
xOut = param(x, "1.0.1.a", "try to override") // won't set
xOut = param(x, "1.0.5.a", "new-setting", true) // get object rather than deepest node.
console.log(xOut);
Naturally, when the numeric keys are greater than 0, you might see some undefined gaps.
Practical uses of this might be
function AddNote(book, page, line) {
// assume a global global notes collection
var myNotes = param(allNotes, [book, page, line], []);
myNotes.push('This was a great twist!')
return myNotes;
}
var allNotes = {}
var youthfulHopes = AddNote('A Game of Thrones', 4, 2, "I'm already hooked, at least I won't have to wait long for the books to come out!");
console.log(allNotes)
// {"A Game of Thrones": [undefined, undefined, undefined, undefined, [undefined, undefined, ["I'm already hooked, at least I won't have to wait long for the books to come out!"]]]}
console.log(youthfulHopes)
// ["I'm already hooked, at least I won't have to wait long for the books to come out!"]
function initPath(obj, path) {
path.split('.').reduce((o, key) => (
Object.assign(o, {[key]: Object(o[key])}),
o[key]
), obj);
return obj;
}
Usage
const obj = { a: { b: 'value1' } };
initPath(obj, 'a.c.d').a.c.d='value2';
/*
{
"a": {
"b": "value1",
"c": {
"d": "value2"
}
}
}
*/
simple answer. on es6, im using this
const assign = (obj, path, value) => {
let keyPath = path.split('.')
let lastKeyIndex = keyPath.length - 1
for (let i = 0; i < lastKeyIndex; ++i) {
let key = keyPath[i]
if (!(key in obj)) {
obj[key] = {}
}
obj = obj[key]
}
obj[keyPath[lastKeyIndex]] = value
}
example json
const obj = {
b: 'hello'
}
you can add new key
assign(obj, 'c.d.e', 'this value')
and you get like bellow
console.log(obj)
//response example
obj = {
b: 'hello',
c: {
d: {
e: 'this value'
}
}
}
function createObj(keys, value) {
let obj = {}
let schema = obj
keys = keys.split('.')
for (let i = 0; i < keys.length - 1; i++) {
schema[keys[i]] = {}
schema = schema[keys[i]]
}
schema[keys.pop()] = value
return obj
}
let keys = 'value1.value2.value3'
let value = 'Hello'
let obj = createObj(keys, value)
Eval is probably overkill but the result is simple to visualize, with no nested loops or recursion.
function buildDir(obj, path){
var paths = path.split('_');
var final = paths.pop();
for (let i = 1; i <= paths.length; i++) {
var key = "obj['" + paths.slice(0, i).join("']['") + "']"
console.log(key)
eval(`${key} = {}`)
}
eval(`${key} = '${final}'`)
return obj
}
var newSettingName = "Modules_Video_Plugin_JWPlayer";
var Settings = buildDir( {}, newSettingName );
Basically you are progressively writing a string "obj['one']= {}", "obj['one']['two']"= {} and evaling it;

Editing a JSON value in varying depth [duplicate]

I have an object that could be any number of levels deep and could have any existing properties.
For example:
var obj = {
db: {
mongodb: {
host: 'localhost'
}
}
};
On that I would like to set (or overwrite) properties like so:
set('db.mongodb.user', 'root');
// or:
set('foo.bar', 'baz');
Where the property string can have any depth, and the value can be any type/thing.
Objects and arrays as values don't need to be merged, should the property key already exist.
Previous example would produce following object:
var obj = {
db: {
mongodb: {
host: 'localhost',
user: 'root'
}
},
foo: {
bar: baz
}
};
How can I realize such a function?
This function, using the arguments you specified, should add/update the data in the obj container. Note that you need to keep track of which elements in obj schema are containers and which are values (strings, ints, etc.) otherwise you will start throwing exceptions.
obj = {}; // global object
function set(path, value) {
var schema = obj; // a moving reference to internal objects within obj
var pList = path.split('.');
var len = pList.length;
for(var i = 0; i < len-1; i++) {
var elem = pList[i];
if( !schema[elem] ) schema[elem] = {}
schema = schema[elem];
}
schema[pList[len-1]] = value;
}
set('mongo.db.user', 'root');
Lodash has a _.set() method.
_.set(obj, 'db.mongodb.user', 'root');
_.set(obj, 'foo.bar', 'baz');
I just write a small function using ES6 + recursion to achieve the goal.
updateObjProp = (obj, value, propPath) => {
const [head, ...rest] = propPath.split('.');
!rest.length
? obj[head] = value
: this.updateObjProp(obj[head], value, rest.join('.'));
}
const user = {profile: {name: 'foo'}};
updateObjProp(user, 'fooChanged', 'profile.name');
I used it a lot on react to update state, it worked pretty well for me.
We can use a recursion function:
/**
* Sets a value of nested key string descriptor inside a Object.
* It changes the passed object.
* Ex:
* let obj = {a: {b:{c:'initial'}}}
* setNestedKey(obj, ['a', 'b', 'c'], 'changed-value')
* assert(obj === {a: {b:{c:'changed-value'}}})
*
* #param {[Object]} obj Object to set the nested key
* #param {[Array]} path An array to describe the path(Ex: ['a', 'b', 'c'])
* #param {[Object]} value Any value
*/
export const setNestedKey = (obj, path, value) => {
if (path.length === 1) {
obj[path] = value
return
}
return setNestedKey(obj[path[0]], path.slice(1), value)
}
It's more simple!
A bit late but here's a non-library, simpler answer:
/**
* Dynamically sets a deeply nested value in an object.
* Optionally "bores" a path to it if its undefined.
* #function
* #param {!object} obj - The object which contains the value you want to change/set.
* #param {!array} path - The array representation of path to the value you want to change/set.
* #param {!mixed} value - The value you want to set it to.
* #param {boolean} setrecursively - If true, will set value of non-existing path as well.
*/
function setDeep(obj, path, value, setrecursively = false) {
path.reduce((a, b, level) => {
if (setrecursively && typeof a[b] === "undefined" && level !== path.length){
a[b] = {};
return a[b];
}
if (level === path.length){
a[b] = value;
return value;
}
return a[b];
}, obj);
}
This function I made can do exactly what you need and a little more.
lets say we want to change the target value that is deeply nested in this object:
let myObj = {
level1: {
level2: {
target: 1
}
}
}
So we would call our function like so:
setDeep(myObj, ["level1", "level2", "target1"], 3);
will result in:
myObj = {
level1: {
level2: {
target: 3
}
}
}
Setting the set recursively flag to true will set objects if they don't exist.
setDeep(myObj, ["new", "path", "target"], 3, true);
will result in this:
obj = myObj = {
new: {
path: {
target: 3
}
},
level1: {
level2: {
target: 3
}
}
}
Inspired by #bpmason1's answer:
function leaf(obj, path, value) {
const pList = path.split('.');
const key = pList.pop();
const pointer = pList.reduce((accumulator, currentValue) => {
if (accumulator[currentValue] === undefined) accumulator[currentValue] = {};
return accumulator[currentValue];
}, obj);
pointer[key] = value;
return obj;
}
Example:
const obj = {
boats: {
m1: 'lady blue'
}
};
leaf(obj, 'boats.m1', 'lady blue II');
leaf(obj, 'boats.m2', 'lady bird');
console.log(obj); // { boats: { m1: 'lady blue II', m2: 'lady bird' } }
I came up with my own solution using pure es6 and recursion that doesn't mutate the original object.
const setNestedProp = (obj = {}, [first, ...rest] , value) => ({
...obj,
[first]: rest.length
? setNestedProp(obj[first], rest, value)
: value
});
const result = setNestedProp({}, ["first", "second", "a"],
"foo");
const result2 = setNestedProp(result, ["first", "second", "b"], "bar");
console.log(result);
console.log(result2);
Lodash has a method called update that does exactly what you need.
This method receives the following parameters:
The object to update
The path of the property to update (the property can be deeply nested)
A function that returns the value to update (given the original value as a parameter)
In your example it would look like this:
_.update(obj, 'db.mongodb.user', function(originalValue) {
return 'root'
})
ES6 has a pretty cool way to do this too using Computed Property Name and Rest Parameter.
const obj = {
levelOne: {
levelTwo: {
levelThree: "Set this one!"
}
}
}
const updatedObj = {
...obj,
levelOne: {
...obj.levelOne,
levelTwo: {
...obj.levelOne.levelTwo,
levelThree: "I am now updated!"
}
}
}
If levelThree is a dynamic property i.e. to set any of the property in levelTwo, you can use [propertyName]: "I am now updated!" where propertyName holds the name of the property in levelTwo.
I needed to achieve the same thing, but in Node.js...
So, I found this nice module: https://www.npmjs.com/package/nested-property
Example:
var mod = require("nested-property");
var obj = {
a: {
b: {
c: {
d: 5
}
}
}
};
console.log(mod.get(obj, "a.b.c.d"));
mod.set(obj, "a.b.c.d", 6);
console.log(mod.get(obj, "a.b.c.d"));
I created gist for setting and getting obj values by string based on correct answer. You can download it or use it as npm/yarn package.
// yarn add gist:5ceba1081bbf0162b98860b34a511a92
// npm install gist:5ceba1081bbf0162b98860b34a511a92
export const DeepObject = {
set: setDeep,
get: getDeep
};
// https://stackoverflow.com/a/6491621
function getDeep(obj: Object, path: string) {
path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
path = path.replace(/^\./, ''); // strip a leading dot
const a = path.split('.');
for (let i = 0, l = a.length; i < l; ++i) {
const n = a[i];
if (n in obj) {
obj = obj[n];
} else {
return;
}
}
return obj;
}
// https://stackoverflow.com/a/18937118
function setDeep(obj: Object, path: string, value: any) {
let schema = obj; // a moving reference to internal objects within obj
const pList = path.split('.');
const len = pList.length;
for (let i = 0; i < len - 1; i++) {
const elem = pList[i];
if (!schema[elem]) {
schema[elem] = {};
}
schema = schema[elem];
}
schema[pList[len - 1]] = value;
}
// Usage
// import {DeepObject} from 'somePath'
//
// const obj = {
// a: 4,
// b: {
// c: {
// d: 2
// }
// }
// };
//
// DeepObject.set(obj, 'b.c.d', 10); // sets obj.b.c.d to 10
// console.log(DeepObject.get(obj, 'b.c.d')); // returns 10
Extending the accepted answer provided by #bpmason1, to support arrays in string path e.g. string path can be 'db.mongodb.users[0].name' and 'db.mongodb.users[1].name'.
It will set the property value, which if doesn't exist, will be created.
var obj = {};
function set(path, value) {
var schema = obj;
var keysList = path.split('.');
var len = keysList.length;
for (var i = 0; i < len - 1; i++) {
var key = keysList[i];
// checking if key represents an array element e.g. users[0]
if (key.includes('[')) {
//getting propertyName 'users' form key 'users[0]'
var propertyName = key.substr(0, key.length - key.substr(key.indexOf("["), key.length - key.indexOf("[")).length);
if (!schema[propertyName]) {
schema[propertyName] = [];
}
// schema['users'][getting index 0 from 'users[0]']
if (!schema[propertyName][parseInt(key.substr(key.indexOf("[") + 1, key.indexOf("]") - key.indexOf("[") - 1))]) {
// if it doesn't exist create and initialise it
schema = schema[propertyName][parseInt(key.substr(key.indexOf("[") + 1, key.indexOf("]") - key.indexOf("[") - 1))] = {};
} else {
schema = schema[propertyName][parseInt(key.substr(key.indexOf("[") + 1, key.indexOf("]") - key.indexOf("[") - 1))];
}
continue;
}
if (!schema[key]) {
schema[key] = {};
}
schema = schema[key];
} //loop ends
// if last key is array element
if (keysList[len - 1].includes('[')) {
//getting propertyName 'users' form key 'users[0]'
var propertyName = keysList[len - 1].substr(0, keysList[len - 1].length - keysList[len - 1].substr(keysList[len - 1].indexOf("["), keysList[len - 1].length - keysList[len - 1].indexOf("[")).length);
if (!schema[propertyName]) {
schema[propertyName] = [];
}
// schema[users][0] = value;
schema[propertyName][parseInt(keysList[len - 1].substr(keysList[len - 1].indexOf("[") + 1, keysList[len - 1].indexOf("]") - keysList[len - 1].indexOf("[") - 1))] = value;
} else {
schema[keysList[len - 1]] = value;
}
}
// will create if not exist
set("mongo.db.users[0].name.firstname", "hii0");
set("mongo.db.users[1].name.firstname", "hii1");
set("mongo.db.users[2].name", {
"firstname": "hii2"
});
set("mongo.db.other", "xx");
console.log(obj);
// will set if exist
set("mongo.db.other", "yy");
console.log(obj);
Here's a solution using ES 12
function set(obj = {}, key, val) {
const keys = key.split('.')
const last = keys.pop()
keys.reduce((o, k) => o[k] ??= {}, obj)[last] = val
}
(For older versions of javascript, you can do do o[k] || o[k] = {} in the reduce instead)
First, we set keys to be an array of everything but the last key.
Then in the reduce, the accumulator goes one level deeper into obj
each time, initializing it to an empty object if it the value at that key is not defined.
Finally, we set the value at the last key to val.
If you only need to change deeper nested objects, then another method could be to reference the object. As JS objects are handled by their references, you can create a reference to an object you have string-key access to.
Example:
// The object we want to modify:
var obj = {
db: {
mongodb: {
host: 'localhost',
user: 'root'
}
},
foo: {
bar: baz
}
};
var key1 = 'mongodb';
var key2 = 'host';
var myRef = obj.db[key1]; //this creates a reference to obj.db['mongodb']
myRef[key2] = 'my new string';
// The object now looks like:
var obj = {
db: {
mongodb: {
host: 'my new string',
user: 'root'
}
},
foo: {
bar: baz
}
};
Another approach is to use recursion to dig through the object:
(function(root){
function NestedSetterAndGetter(){
function setValueByArray(obj, parts, value){
if(!parts){
throw 'No parts array passed in';
}
if(parts.length === 0){
throw 'parts should never have a length of 0';
}
if(parts.length === 1){
obj[parts[0]] = value;
} else {
var next = parts.shift();
if(!obj[next]){
obj[next] = {};
}
setValueByArray(obj[next], parts, value);
}
}
function getValueByArray(obj, parts, value){
if(!parts) {
return null;
}
if(parts.length === 1){
return obj[parts[0]];
} else {
var next = parts.shift();
if(!obj[next]){
return null;
}
return getValueByArray(obj[next], parts, value);
}
}
this.set = function(obj, path, value) {
setValueByArray(obj, path.split('.'), value);
};
this.get = function(obj, path){
return getValueByArray(obj, path.split('.'));
};
}
root.NestedSetterAndGetter = NestedSetterAndGetter;
})(this);
var setter = new this.NestedSetterAndGetter();
var o = {};
setter.set(o, 'a.b.c', 'apple');
console.log(o); //=> { a: { b: { c: 'apple'}}}
var z = { a: { b: { c: { d: 'test' } } } };
setter.set(z, 'a.b.c', {dd: 'zzz'});
console.log(JSON.stringify(z)); //=> {"a":{"b":{"c":{"dd":"zzz"}}}}
console.log(JSON.stringify(setter.get(z, 'a.b.c'))); //=> {"dd":"zzz"}
console.log(JSON.stringify(setter.get(z, 'a.b'))); //=> {"c":{"dd":"zzz"}}
Late to the party - here's a vanilla js function that accepts a path as an argument and returns the modified object/json
let orig_json = {
string: "Hi",
number: 0,
boolean: false,
object: {
subString: "Hello",
subNumber: 1,
subBoolean: true,
subObject: {
subSubString: "Hello World"
},
subArray: ["-1", "-2", "-3"]
},
array: ["1", "2", "3"]
}
function changeValue(obj_path, value, json) {
let keys = obj_path.split(".")
let obj = { ...json },
tmpobj = {},
prevobj = {}
for (let x = keys.length - 1; x >= 0; x--) {
if (x == 0) {
obj[keys[0]] = tmpobj
} else {
let toeval = 'json.' + keys.slice(0, x).join('.');
prevobj = { ...tmpobj
}
tmpobj = eval(toeval);
if (x == keys.length - 1) tmpobj[keys[x]] = value
else {
tmpobj[keys[x]] = prevobj
}
}
}
return obj
}
let newjson = changeValue("object.subObject.subSubString", "Goodbye world", orig_json);
console.log(newjson)
Another solution to add or override properties:
function propertySetter(property, value) {
const sampleObject = {
string: "Hi",
number: 0,
boolean: false,
object: {
subString: "Hello",
subNumber: 1,
subBoolean: true,
subObject: {
subSubString: "Hello World",
},
subArray: ["-1", "-2", "-3"],
},
array: ["1", "2", "3"],
};
const keys = property.split(".");
const propertyName = keys.pop();
let propertyParent = sampleObject;
while (keys.length > 0) {
const key = keys.shift();
if (!(key in propertyParent)) {
propertyParent[key] = {};
}
propertyParent = propertyParent[key];
}
propertyParent[propertyName] = value;
return sampleObject;
}
console.log(propertySetter("object.subObject.anotherSubString", "Hello you"));
console.log(propertySetter("object.subObject.subSubString", "Hello Earth"));
console.log(propertySetter("object.subObject.nextSubString.subSubSubString", "Helloooo"));
Inspired by ImmutableJS setIn method which will never mutate the original.
This works with mixed array and object nested values.
function setIn(obj = {}, [prop, ...rest], value) {
const newObj = Array.isArray(obj) ? [...obj] : {...obj};
newObj[prop] = rest.length ? setIn(obj[prop], rest, value) : value;
return newObj;
}
var obj = {
a: {
b: {
c: [
{d: 5}
]
}
}
};
const newObj = setIn(obj, ["a", "b", "c", 0, "x"], "new");
//obj === {a: {b: {c: [{d: 5}]}}}
//newObj === {a: {b: {c: [{d: 5, x: "new"}]}}}
As #aheuermann sed, you can use set from lodash library,
However, if you don't want to add lodash to your project for some reason you can use a recursion function that sets/overrides a value in an object.
/**
* recursion function that called in main function
* #param obj initial JSON
* #param keysList array of keys
* #param value value that you want to set
* #returns final JSON
*/
function recursionSet(obj, keysList, value) {
const key = keysList[0]
if (keysList.length === 1) return { ...obj, [key]: value }
return { ...obj, [key]: (recursionSet(obj?.[key] || {}, keysList.slice(1), value)) }
}
/**
* main function that you can call for set a value in an object by nested keys
* #param obj initial JSON
* #param keysString nested keys that seprated by "."
* #param value value that you want to set
* #returns final JSON
*/
function objectSet(obj, keysString, value) {
return recursionSet(obj, keysString.split('.'), value)
}
// simple usage
const a1 = {}
console.log('simple usage:', objectSet(a1, "b.c.d", 5))
// keep the initial data
const a2 = {b:{e: 8}}
console.log('keep the initial data:', objectSet(a2, "b.c.d", 5))
// override data
const a3 = {b:{e: 8, c:2}}
console.log('override data:', objectSet(a3, "b.c.d", 5))
// complex value
const a4 = {b:{e: 8, c:2}}
console.log('complex value:', objectSet(a4, "b.c.d", {f:12}))
If you would like a function that required prior properties to exist, then you could use something like this, it would also return a flag stating whether it managed to find and set the nested property.
function set(obj, path, value) {
var parts = (path || '').split('.');
// using 'every' so we can return a flag stating whether we managed to set the value.
return parts.every((p, i) => {
if (!obj) return false; // cancel early as we havent found a nested prop.
if (i === parts.length - 1){ // we're at the final part of the path.
obj[parts[i]] = value;
}else{
obj = obj[parts[i]]; // overwrite the functions reference of the object with the nested one.
}
return true;
});
}
JQuery has an extend method:
https://api.jquery.com/jquery.extend/
just pass the overwrites as an object and it will merge the two.
Inspired by ClojureScript's assoc-in (https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L5280), using recursion:
/**
* Associate value (v) in object/array (m) at key/index (k).
* If m is falsy, use new object.
* Returns the updated object/array.
*/
function assoc(m, k, v) {
m = (m || {});
m[k] = v;
return m;
}
/**
* Associate value (v) in nested object/array (m) using sequence of keys (ks)
* to identify the path to the nested key/index.
* If one of the values in the nested object/array doesn't exist, it adds
* a new object.
*/
function assoc_in(m={}, [k, ...ks], v) {
return ks.length ? assoc(m, k, assoc_in(m[k], ks, v)) : assoc(m, k, v);
}
/**
* Associate value (v) in nested object/array (m) using key string notation (s)
* (e.g. "k1.k2").
*/
function set(m, s, v) {
ks = s.split(".");
return assoc_in(m, ks, v);
}
Note:
With the provided implementation,
assoc_in({"a": 1}, ["a", "b"], 2)
returns
{"a": 1}
I would prefer that it throw an error in this case. If desired, you can add a check in assoc to verify m is either an object or array and throw an error otherwise.
I tried to write this set method in short, it may help someone!
function set(obj, key, value) {
let keys = key.split('.');
if(keys.length<2){ obj[key] = value; return obj; }
let lastKey = keys.pop();
let fun = `obj.${keys.join('.')} = {${lastKey}: '${value}'};`;
return new Function(fun)();
}
var obj = {
"hello": {
"world": "test"
}
};
set(obj, "hello.world", 'test updated');
console.log(obj);
set(obj, "hello.world.again", 'hello again');
console.log(obj);
set(obj, "hello.world.again.onece_again", 'hello once again');
console.log(obj);
const set = (o, path, value) => {
const props = path.split('.');
const prop = props.shift()
if (props.length === 0) {
o[prop] = value
} else {
o[prop] = o[prop] ?? {}
set(o[prop], props.join('.'), value)
}
}
in case you want to deeply update or insert an object
try this :-
let init = {
abc: {
c: {1: 2, 3: 5, 0: {l: 3}},
d: 100
}
}
Object.prototype.deepUpdate = function(update){
let key = Object.keys(update);
key.forEach((k) => {
if(typeof update[key] == "object"){
this[k].deepUpdate(update[key], this[k])
}
else
this[k] = update[k]
})
}
init.deepUpdate({abc: {c: {l: 10}}})
console.log(init)
but make sure it will change the original object, you can make it to not change the original object :
JSON.parse(JSON.stringify(init)).deepUpdate({abc: {c: {l: 10}}})
Improving on bpmason1's answer:
-adds a get() function.
-It does not require to define global storage object
-It is accessible from same domain iFrames
function set(path, value)
{
var schema = parent.document;
path="data."+path;
var pList = path.split('.');
var len = pList.length;
for(var i = 0; i < len-1; i++)
{
if(!schema[pList[i]])
schema[pList[i]] = {}
schema = schema[pList[i]];
}
schema[pList[len-1]] = value;
}
function get(path)
{
path="data."+path;
var schema=parent.document;
var pList = path.split('.');
for(var i = 0; i < pList.length; i++)
schema = schema[pList[i]];
return schema;
}
set('mongo.db.user', 'root');
set('mongo.db.name', 'glen');
console.log(get('mongo.db.name')); //prints 'glen'
Sometimes if the key also has dots (.) it its string this may pose a problem. As even that single key will now get split into various keys.
It is best to store the key path in an array, like so: ['db','mongodb','user'] and assign the value dynamically with the below function.
function set(obj, path, value) {
var schema = obj;
var pList = path.slice();
var len = pList.length;
for (var i = 0; i < len - 1; i++) {
var elem = pList[i];
if (!schema[elem]) schema[elem] = {};
schema = schema[elem];
}
schema[pList[len - 1]] = value;
}
let path = ['db','mongodb','user'];
set(obj, path, 'root');
I want to leave my answer for this interesting topic. Creating a function that sets dynamic properties for an object can be difficult.
const entity = {
haveDogs: true,
dogs: ['Maya', 'Perla']
}
function isObject(obj) {
return obj instanceof Object && obj.constructor === Object;
}
function setSchema(key, schema, value) {
if (!isObject(value)) {
schema[key] = value;
return
}
if (!schema[key]) schema[key] = {}
schema[key] = mutate(schema[key], value);
}
function mutate(obj, newObjData) {
const keys = Object.keys(newObjData)
for (const key of keys) {
let schema = obj
const list = key.split('.')
const value = newObjData[key]
const total = list.length - 1
if (list.length === 1) {
setSchema(key, schema, value)
continue
}
for (let i = 0; i < total; i++) {
const elem = list[i];
if (!schema[elem]) schema[elem] = {}
schema = schema[elem]
}
const subField = list[total]
setSchema(subField, schema, value)
}
return obj
}
mutate(entity, {
haveDogs: false,
'pet1.pet2.pet3.pet4.pet5': 'pets',
'bestFriends.list': ['Maya', 'Lucas'],
friends: {
'whitelist.permitted': ['Maya', 'Perla'],
'party.blocked': ['Juan', 'Trump']
}
})
console.log('[entity]', entity)

Converting array into object in javascript [duplicate]

I try to create an object with a value for the last key. I just have an Array with the keys and the value but dont know how it will be possible to create an object without use references in javascript.
As far as I know there isnt a way to create a reference of a variable in javascript.
This is what i have:
var value = 'test';
var keys = ['this', 'is', 'a', 'test'];
This is what i want:
myObject: {
this : {
is: {
a : {
test : 'test'
}
}
}
}
Any idea how i can do this the best way in JavaScript ?
How about this...
const value = 'test'
const keys = ['this', 'is', 'a', 'test']
const myObject = keys.reduceRight((p, c) => ({ [c]: p }), value)
console.info(myObject)
Or, if you're not a fan of object literal key shortcuts and arrow functions...
keys.reduceRight(function(p, c) {
var o = {};
o[c] = p;
return o;
}, value);
See Array.prototype.reduceRight() - Polyfill if you need IE <= 8 support.
With this:
var curObj = myObject = {};
for(var i=0; i<keys.length-1; i++)
curObj = curObj[keys[i]] = {};
curObj[value] = keys[i];
Outputs:
{
this : {
is: {
a : {
Test : 'test'
}
}
}
}
As opposed to the other answers, this outputs exactly what you asked for.
Cheers
var o={}, c=o;
var value = 'Test';
var keys = 'this is a test'.split(' ');
for (var i=0; i<keys.length-1; ++i) c = c[keys[i]] = {};
c[keys[i]] = value;
console.log(JSON.stringify(o));
// {"this":{"is":{"a":{"test":"Test"}}}}
If you want the output like
{
this : {
is: {
a : 'test'
}
}
}
Do the following
var keys = ['this', 'is', 'a', 'test'];
var output = keys.reduceRight((p,c)=>({[c]:p}))
console.log(output)
And the output will be like
{ this: { is: { a: 'test' } } }

Access an object with the keys in an array

I have an object and I want to access this:
obj['example']['example-2']['example-3'];
problem is I have an array where I store those keys:
arr = ['example', 'example-2', 'example-3'];
but this array can be of variable length so maybe it's just 3 keys maybe there are 6.
How can I achieve this without hardcoding each case, for example:
if(arr.length == 1){
//obj[arr[0]];
}else if (arr.length == 2){
//obj[arr[0]][arr[1]];
}
etc..
You could use the path and reduce the object.
function getValue(o, path) {
return path.reduce(function (o, k) {
return (o || {})[k];
}, o);
}
var obj = { example: { 'example-2': { 'example-3': 42 } } },
arr = ['example', 'example-2', 'example-3'];
console.log(getValue(obj, arr));
For other people, if you didn't want to use reduce you could use a simple for loop.
function getNestedValue(obj, keys) {
var ret = obj[keys[0]];
for (var i = 1; i < keys.length; i++) {
ret = ret[keys[i]];
}
return ret;
}
var ob = {
HelloWorld: {
James: {
Ted: 55
}
}
};
var depth = ["HelloWorld", "James", "Ted"];
You could use this object-dig package :
This allows you to use method like Ruby's hash#dig in JavaScript.
var dig = require('object-dig');
var object = { a: { b: { c: 'c' } } };
dig(object, 'a', 'b');
// => { c: 'c' }
dig(object, 'a', 'b', 'c');
// => 'c'
dig(object, 'a', 'unknownProp', 'c');
// =>undefined

What is the best way in JavaScript to trim down the properties of an object?

Situation:
I have an object like
{ prop_1 : val_1, prop_2 : val_2, prop_3 : val_3 , ..., prop_N : val_N }
and I want to remove all properties that aren't prop_i, prop_j or prop_K ?
What is the best way to do this other than the "brute force" way of
var original = { prop_1 : val_1, prop_2 : val_2, prop_3 : val_3 , ..., prop_N : val_N };
var newguy = { prop_i : original.prop_i, prop_j : original.prop_j, prop_k : original.prop_k };
original = newguy;
????
Well you can do a function to help you do that.
(function() {
'use strict';
function copyOnly(obj, keysToPreserve) {
var result = {};
for (var i = 0, length = keysToPreserve.length; i < length; ++i) {
var key = keysToPreserve[i];
result[key] = obj[key];
}
return result;
}
function copyExclude(obj, keysToExclude) {
var result = {};
for (var key in obj) {
if (obj.hasOwnProperty(key) && keysToExclude.indexOf(key) === -1) { // -1 means key doesn't exist in keysToExclude
result[key] = obj[key];
}
}
return result;
}
var original = {
a: '1',
b: '2',
c: '3',
d: '4',
e: '5'
};
var toPreserve = ['a', 'b', 'c'];
var result1 = copyOnly(original, toPreserve);
var toExclude = ['d', 'e'];
var result2 = copyExclude(original, toExclude);
// result1 will have the same structure as result2
document.getElementById('result').innerHTML = 'result1 = ' + JSON.stringify(result1) + '\n' + 'result2 = ' + JSON.stringify(result2);
})();
<pre id="result"></pre>
Here is a non-brute-force way. It uses a whitelist, iterates over them, and copies values from "oldguy".
var oldguy = {
"prop_1": 1,
"prop_2": 2,
"prop_3": 3,
"prop_i": "i",
"prop_j": "j",
"prop_k": "k",
"prop_N": "N",
"prop_z": "Z"
};
var newguy = {};
var keys_to_include = ['prop_i', 'prop_j', 'prop_k'];
keys_to_include.forEach(function(k){
newguy[k] = oldguy[k];
});
$('#output').html( JSON.stringify(newguy,null,' ') );
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<pre><code id="output"></code></pre>
Simple .forEach and .indexOf over Object.keys to delete non-matches
function cleanExcept(o, whitelist) {
Object.keys(o).forEach(k => whitelist.indexOf(k) !== -1 ? 0 : delete o[k]);
return o;
}
var o = {foo: 'foo', bar: 'bar', fizz: 'fizz', buzz: 'buzz'};
cleanExcept(o, ['foo', 'fizz']); // Object {foo: "foo", fizz: "fizz"}
Using an Object cache instead of .indexOf, as per #dandavis
function cleanExcept(o, whitelist) {
var w = {};
whitelist.forEach(k => w[k] = true);
Object.keys(o).forEach(k => w[k] ? 0 : delete o[k]);
return o;
}
Modifying this cache just take the values you want and return it (i.e. you get a new object reference)
function cleanExcept(o, whitelist) {
var w = {};
whitelist.forEach(k => !(k in o) ? 0 : w[k] = o[k]);
return w;
}
In ES6, you can write
({prop_i, prop_j, prop_K}) => ({prop_i, prop_j, prop_K})(original)
This works by defining a function which deconstructs its arguments into certain property values, and returns an object with those values. Then call the function on the input object.
See One-liner to take some properties from object in ES 6.

Categories