5

If I have a JS object, and I'd like to create a new object which copies over all properties except for a blacklist of properties that need to be filtered out, what's the simplest way to do it?

So I would do something like

originalObject.copyAndFilter('a', 'b')

Which would take the original object, copy it, and make sure properties a and b aren't in the new object.

I'm using ES2015/2016 through Babel so if it provides an even simpler way that would work too.

user779159
  • 9,034
  • 14
  • 59
  • 89
  • 2
    shallow or deep copy? are the filtered keys anywhere (any depth) or just at the first level? – Jaromanda X Oct 11 '16 at 07:57
  • a cheap and easy way to do a deep copy on simple objects is `var copy = JSON.parse(JSON.stringify(object))` - amongst the javascript Primitives, `Date`, `Symbol` and `undefined` wont work this simply ... this leaves only booleans, numbers and strings that will work using this "hack" - though, there are ways around that too, but that's even more of a hack – Jaromanda X Oct 11 '16 at 08:23
  • http://youmightnotneedjquery.com/#deep_extend (or just extend), just add a `filter` argument (array of key names) to the function signature and use `if (obj.hasOwnProperty(key) && filter.indexOf(key) < 0)`? – Simon Hänisch Oct 11 '16 at 10:52

6 Answers6

2

You could use Set and delete the unwanted properties.

var object = { a: 1, b: 2, c: 3, d: 4 },
    filteredObject = {},
    p = new Set(Object.keys(object));
    blacklist = ['a', 'b'];

blacklist.forEach(a => p.delete(a));

[...p].forEach(k => filteredObject[k] = object[k]);
console.log(filteredObject);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
1

Well, there's no simple native way to do it, I would convert the keys to an array, filter it, then create a new object:

const obj = {a: 'foo', b: 'bar', c: 'baz'};
const blacklist = ['a', 'b'];

const keys = Object.keys(obj);
const filteredKeys = keys.filter(key => !blacklist.includes(key));

const filteredObj = filteredKeys.reduce((result, key) => {
  result[key] = obj[key];
  return result;
}, {});

console.log(filteredObj);
Madara's Ghost
  • 172,118
  • 50
  • 264
  • 308
0

You can just loop over object keys and create a new object. You can even use for...in for it. This will have added advantage of being supported in all browsers.

var obj = {a: 'foo', b: 'bar', c: 'baz'};
var blackListKeys = ['a', 'b','z'];
var obj2 = {}
for(var k in obj){
  if(blackListKeys.indexOf(k) === -1)
    obj2[k] = obj[k];
}
console.log(obj2)
Rajesh
  • 24,354
  • 5
  • 48
  • 79
0

var obj = {foo: 1, a: 2, b:3}
var newObj = {}
var blacklist = ['a', 'b']

for(let [key, value] of Object.entries(obj)) {
  !blacklist.includes(key) && (newObj[key] = value)
}

console.log(newObj)

With fewer variables:

var obj = {foo: 1, a: 2, b: 3}

var newObj = Object.entries(obj)
  .filter(([key, val]) => !['a', 'b'].includes(key))
  .reduce((newObj, [key, value]) => (newObj[key] = value, newObj), {})

console.log(newObj)
Endless
  • 34,080
  • 13
  • 108
  • 131
0

I think Object.assign is the best ES5 utility to make this with little extra logic:

function copyAndFilter(...args){ 
    var copy = Object.assign( {} , this);

    args.forEach(prop => { 
        delete copy[prop];
    }); return copy;
}

Then:

var a = {foo : 'foo' , bar : 'bar'};
a.copyAndFilter = /* function definition above*/
a.copyAndFilter('foo');// { bar : 'bar' }
oligofren
  • 20,744
  • 16
  • 93
  • 180
Sergeon
  • 6,638
  • 2
  • 23
  • 43
0

Here there are two problems:

First one is actually copying the object without references.

If you simply loop against object keys and perform assignments of its values from the original object, if that values are also objects, you end up referencing that objects (or even arrays), not copying itself. So you will need to detect that and recursively call your copy_object function to avoid references.

Fortunately that problem is already solved in some libraries which provides an object extend() function like npm (server side) and jQuery (browser).

The trick consist only on extending new freshly created object:

var dst_object = extend({}, src_object);

The second problem is to avoid undesired keys.

Here there are to possible approaches: One is to fully reimplement beforementioned extend() function in a way that they provide a blacklist functionality.

...and the second (and less error prone) is to simply drop undesired keys after copying the object. If they aren't huge data structures, the overhead will be insignificant.

Example:

// npm install --save extend
var extend = require("extend");
function oClone(target, blacklist) {
    var dest = extend({}, target);
    if (blacklist) {
        for (var i=0; i<blacklist.length; i++) {
            delete (dest[blacklist[i]]);
        };
    };
    return dest;
};
bitifet
  • 3,514
  • 15
  • 37