I have an object. I would like to modify the object (not clone it) by removing all properties except for certain specific properties. For instance, if I started with this o
You can code your own implementation of _.pick
, and use it according to your needs.
Having this snippet of code as the base for the following cases:
const myObj={
p1:123,
p2:321,
p3:{p3_1:1231,p3_2:342},
p4:'23423',
//....
p99:{p99_1:'sadf',p99_2:234},
p100:3434
}
let properties= ['p1','p2', 'p3', 'p100'];
You want a shallow copy (with references to vector values)
const myNewObj = properties.reduce((newObj,property)=>{newObj[property] = myObj[property]; return newObj}, {})
// if we modify the original vector value of 'p3' in `myObj` we will modify the copy as well:
myObj.p3.p3_1 = 99999999999;
console.log(myNewObj); // { p1: 123, p2: 321, p3: { p3_1: 99999999999, p3_2: 42 }, p100: 3434 }
You want a deep copy (losing references to vector values)
You can just use JSON
utilities to that matter.
const myNewObj2 = properties.reduce((newObj,property)=>{newObj[property] = JSON.parse(JSON.stringify(myObj[property])); return newObj},{})
// no matter how hard you modify the original object, you will create a new independent object
myObj.p3.p3_1 = 99999999999;
console.log(myNewObj2) // { p1: 123, p2: 321, p3: { p3_1: 1231, p3_2: 342 }, p100: 3434 }
You could implement a reducer to use it in different scenarios, like this one:
function reduceSelectedProps(origin, properties){
return (newObj,property)=>{
newObj[property] = JSON.parse(JSON.stringify(origin[property]));
return newObj
}
}
So you could have a more elegant reuse of it:
const myNewObj3 = properties.reduce(reduceSelectedProps(myObj, properties),{});
// no matter how hard you modify the original object, you will create a new independent object
myObj.p3.p3_1 = 99999999999;
console.log(myNewObj3) // { p1: 123, p2: 321, p3: { p3_1: 1231, p3_2: 342 }, p100: 3434 }
disclaimers:
This is only an example that does not handle Date
, Set
, Map
or function
values inside the properties. To deal with all these cases (and many others), it needs a really complex function with checks on the prototypes and all that stuff. At this point, consider reusing the work of other developers using any library that could do it. Lodash?
Lodash has a function called pick which does what you're describing. I know that including a library isn't ideal, but you can also cherry-pick functions when using bundles, etc.
var myObj={
p1:123,
p2:321,
p3:{p3_1:1231,p3_2:342},
p4:'23423',
//....
p99:{p99_1:'sadf',p99_2:234},
p100:3434
}
var newObj = _.pick(myObj, 'p1', 'p2', 'p100')
You could use delete
:
for (var k in myObj) {
if (k !== 'p1' && k !== 'p2' && k !== 'p100') {
delete myObj[k];
}
}
An alternative to indexOf
:
var keep = /^p(1|2|100)$/;
for (var k in myObj) {
if (!keep.test(k)) {
delete myObj[k];
}
}
Shorter :
var keep = /^p(1|2|100)$/;
for (var k in myObj) {
keep.test(k) || delete myObj[k];
}
Array to RegExp :
var keep = [1, 2, 100];
keep = new RegExp('^p(' + keep.join('|') + ')$'); // /^p(1|2|100)$/
keep.test('p1'); // true
keep.test('p3'); // false
Useful with a function :
function keep(o, ids) {
var keep = new RegExp('^p(' + ids.join('|') + ')$');
for (var k in o) keep.test(k) || delete o[k];
return o;
}
Usage :
keep(myObj, [1, 2, 100]); // { p1: 123, p2: 321, p100: 3434 }
You don't like regular expressions but still want a bullet proof solution :
function keep(o, keys) {
for (var k in o) contains(keys, k) || delete o[k];
return o;
}
function contains(array, value) {
var i = -1, l = array.length;
while (++i < l) if (array[i] === value) return true;
return false;
}
function prefix(array, prefix) {
var i = -1, l = array.length, output = [];
while (++i < l) output.push(prefix + array[i]);
return output;
}
Usage :
keep(myObj, ['p1', 'p2', 'p100']);
// with a middleman :
var idsToKeep = [1, 2, 100];
keep(myObj, prefix(idsToKeep, 'p'));
Well, I could go on like this infinitely, but it's time to eat.
You could create a view on your first object, some kind of proxy that would only keep the desired properties on sight.
For instance the following function will create a view that allows to both read and write the underlying object, keeping only the choosen properties.
You can make it readonly very easily, by just removing the setter.
You might also want to seal the proxy object, so that no later modification can me made to it.
function createView(obj, propList) {
var proxy = {};
for (var propIndex in propList) {
var prop=propList[propIndex];
Object.defineProperty(proxy, prop,
{ enumerable : true ,
get : getter.bind(obj,prop),
set : setter.bind(obj,prop) } );
}
return proxy;
}
function getter(prop) { return this[prop] ; }
function setter(prop, value) { return this[prop] = value ; }
An example of use would be :
var myObj={
p1:123,
p2:321,
p3:{p3_1:1231,p3_2:342},
p4:'23423',
p99:{p99_1:'sadf',p99_2:234},
p100:3434
};
var objView = createView(myObj, ['p1', 'p2', 'p100']);
Here, objView 'reflects' the desired properties of myObj. You can look at the small jsbin i made here :
http://jsbin.com/munudewa/1/edit?js,console
results :
"on objView, p1:123 p2:321 p100:3434 and p4 (not in view) is : undefined"
"modifiying, on the view, p1 to 1000 and p2 to hello "
"on objView, p1:1000 p2:hello p100:3434 and p4 (not in view) is : undefined"
"modifiying, on the viewed object, p1 to 200 and p2 to bye "
"on objView, p1:200 p2:bye p100:3434 and p4 (not in view) is : undefined"
notice that :
1) you can overwrite an object by its view, only keeping desired properties.
2) you can save in a hidden property / in a closure, the original object, so you can later change the properties you expose.
An object stored in a variable named o
:
var o = { a: 1, b: 2 };
A new reference to this object :
var p = o;
o
and p
both refer to the same object :
o // { a: 1, b: 2 }
p // { a: 1, b: 2 }
o === p // true
Let's update the object through o
:
delete o.b;
o // { a: 1 }
p // { a: 1 }
Let's update the object through p
:
p.b = 2;
o // { a: 1, b: 2 }
p // { a: 1, b: 2 }
As you can see, o
and p
are in sync.
Let's "reinitialize" o
:
o = { a: o.a };
o
and p
now refer to different objects :
o // { a: 1 }
p // { a: 1, b: 2 }
o === p // false
Let's update the object stored in o
:
o.c = 3;
o // { a: 1, c: 3 }
p // { a: 1, b: 2 }
Let's update the object stored in p
:
delete p.a;
o // { a: 1, c: 3 }
p // { b: 2 }
As you can see, o
and p
are not in sync anymore.
The question is : do you want to keep both variables (o
and p
) synchronized? If so, the second code block of VisioN's answer is the right one, otherwise, choose the first code block.