44

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 object:

var myObj={
    p1:123,
    p2:321,
    p3:{p3_1:1231,p3_2:342},
    p4:'23423',
    //....
    p99:{p99_1:'sadf',p99_2:234},
    p100:3434
}

and only want properties p1, p2, and p100, how can I obtain this object:

var myObj={
    p1:123,
    p2:321,
    p100:3434
}

I understand how I could do this with brute force, but would like a more elegant solution.

8
  • What is "brute force"? Commented Mar 5, 2014 at 16:08
  • @Andy. I guess I am looking just for the most elegant solution. Commented Mar 5, 2014 at 16:11
  • 1
    I guess you have a good time User, many things to read ;) Commented Mar 5, 2014 at 16:34
  • 1
    This guy gave one of the best answers first (second code block) : stackoverflow.com/a/22202827/1636522. However, as mentioned by George Jempty, old browsers won't like indexOf. A workaround can be found here though : mdn.beonex.com/en/Core_JavaScript_1.5_Reference/Global_Objects/…. Commented Mar 5, 2014 at 17:08
  • 2
    @Ryan Nice answer. let result = (({ p1, p2, p100 }) => ({ p1, p2, p100 }))(myObj); worked perfect. Not sure whether it is to late to switch my selected answer but will try if you wish to post this as an answer. Thanks Commented May 18, 2021 at 12:10

12 Answers 12

28

You could use this approach:

let result = (({ p1, p2, p100 }) => ({ p1, p2, p100 }))(myObj);

which I learned at https://stackoverflow.com/a/25554551/470749.

But I actually prefer splitting into 2 lines, which seems clearer to me:

const { id, name } = playlist;
return { id, name };
Sign up to request clarification or add additional context in comments.

3 Comments

Also, here the Object loses its reference, i.e. result and myObj will have a separate copy of itself.
I actually prefer splitting into 2 lines, which seems clearer to me: const { id, name } = playlist; return { id, name };
This is a good solution because it's easy to extend, e.g. if you need to map id to ID in the process (for whatever reason): const { id: ID, name } = playlist;
22

This was the first hit when googling 'js keep only certain keys' so might be worth an update.

The most 'elegant' solution might just be using underscore.js

_.pick(myObj, 'p1', 'p2', 'p100')

3 Comments

...or lodash.
@x-yuri Could you elaborate on that pls? Something like lodash.pick or what are you impling? Or is _.pick from lodash itself?
@MattSom My point was that lodash is supposedly more popular these days. About lodash.pick, no, the way you call it is similar in lodash and underscore.js. But it may be lodash.pick, depending on how you make it available to the script.
19

Just re-initialise the object:

myObj = {
    p1:   myObj.p1,
    p2:   myObj.p2,
    p100: myObj.p100
};

Another way is to delete certain properties, which is less effective:

var prop = ['p1', 'p2', 'p100'];
for (var k in myObj) {
    if (prop.indexOf(k) < 0) {
        delete myObj[k];
    }
}

12 Comments

@JuanMendes It won't clone it.
But you still don't have the object you started with
The second code block is effective, unlike the first one. Indeed, what if another variable refers to myObj?
@VisioN What about any other references to the first object ?
The amount of properties is not the problem @user1032531, focus on this sentence : "if you do not need to keep references to the original object". Give me a minute, I'll post an answer to explain this to you.
|
7

You could use delete:

for (var k in myObj) {
    if (k !== 'p1' && k !== 'p2' && k !== 'p100') {
        delete myObj[k];
    }
}

An alternative to indexOf:

var take = /^p(1|2|100)$/;
for (var k in myObj) {
    if (!take.test(k)) {
        delete myObj[k];
    }
}

Shorter:

var take = /^p(1|2|100)$/;
for (var k in myObj) {
    take.test(k) || delete myObj[k];
}

Array to RegExp:

var take = [1, 2, 100];
take = new RegExp('^p(' + take.join('|') + ')$'); // /^p(1|2|100)$/
take.test('p1'); // true
take.test('p3'); // false

Useful in a function:

function take(o, ids) {
    var take = new RegExp('^p(' + ids.join('|') + ')$');
    for (var k in o) take.test(k) || delete o[k];
    return o;
}

Usage:

take(myObj, [1, 2, 100]); // { p1: 123, p2: 321, p100: 3434 }

If you don't like regular expressions:

function take(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:

take(myObj, ['p1', 'p2', 'p100']);
// with a middleman :
var idsToTake = [1, 2, 100];
take(myObj, prefix(idsToTake, 'p'));

Comments

6

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')

Comments

5
var myObj = {a: 1, b: 2, c:3};

function keepProps(obj, keep) {
    for (var prop in obj) {
        if (keep.indexOf( prop ) == -1) {
            delete obj[prop];
        }             
    }
}

keepProps(myObj, ['a', 'b']);
console.log(myObj);

http://jsfiddle.net/mendesjuan/d8Sp3/2/

8 Comments

Looks like brute force to me, and he doesn't want that
@tewathia Please elaborate "brute force". This is the most dynamic way to do it based on OP's question.
Well, you are going through All the properties of the object(I agree this really is the best way of getting the desired object without cloning it), and I believe that's what he meant by "brute force"
Why do you use 2 nested loops and indexOf?
@GeorgeJempty You're very persnickety ;) My answer is cross browser compatible by the way :D
|
2

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.

Comments

2

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'];

case 1:

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 }​​​​​

case 2:

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 }​​​​​

Reusing case 2 with a function

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?

Comments

0

Pass a map of whitelisted keys into an IIFE (immediately invoked function expression); not just elegant but also flexible IMO (especially if moved off into a function not unlike in Juan Mendes' answer)

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 myObj = (function(origObj, whiteListMap) {
    for (var prop in origObj) {
        if (!whiteListMap[prop]) {
            delete origObj[prop];
        }
    }
    return myObj;
})(myObj, {'p1': 1, 'p2': 1, 'p100': 1});

console.log(JSON.stringify(myObj)); //{"p1":123,"p2":321,"p100":3434}

3 Comments

If the whitelisted property's value is null, 0, '', or undefined it won't be in the built object.
@GameAlchemist, that is why I always set them to 1. Another approach is to pass in a whitelist array but then create an object out of it; this is a bit easier on the user. I disagree with using indexOf on the array though; even natively it requires looping instead of a simple hash lookup
you can use 'in' to avoid having to set to one : if (prop in whiteListMap).
0

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.

Comments

0

I Made this short solution for case where I have an array with objects.

so consider the array below?

arr=[{"a1":"A1","b1":"B1"},{"a1":"Z1","b1":"X1"}];
console.log(arr);

I want to keep only "b1" properties of all objects.

You can use map() and delete for that as follows:

arr=[{"a1":"A1","b1":"B1"},{"a1":"Z1","b1":"X1"}];
arr=arr.map(function(d){
        delete d["a1"];
        return d;

    });
console.log(arr);

result is an array with objects but only "b1" properties.

Comments

0

just a single line of pure js code

var myObj={
    p1:123,
    p2:321,
    p3:{p3_1:1231,p3_2:342},
    p4:'23423',
    //....
    p99:{p99_1:'sadf',p99_2:234},
    p100:3434
}

Object.keys(myObj).forEach(key => { if(!["p1","p2","p100"].includes(key)) delete myObj[key]; })

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.