JavaScript: filter () for objects

ECMAScript 5 has a filter() prototype for Array types, but not Object , if I understand correctly.

How do I implement filter() for an Object in JavaScript?

Let's say I have this object:

 var foo = { bar: "Yes" }; 

And I want to write filter() that works on Object s:

 Object.prototype.filter = function(predicate) { var result = {}; for (key in this) { if (this.hasOwnProperty(key) && !predicate(this[key])) { result[key] = this[key]; } } return result; }; 

This works when I use it in the next demo, but when I add it to my site that uses jQuery 1.5 and jQuery UI 1.8.9, I get JavaScript errors in FireBug.

 Object.prototype.filter = function(predicate) { var result = {}; for (key in this) { if (this.hasOwnProperty(key) && !predicate(this[key])) { console.log("copying"); result[key] = this[key]; } } return result; }; var foo = { bar: "Yes", moo: undefined }; foo = foo.filter(function(property) { return typeof property === "undefined"; }); document.getElementById('disp').innerHTML = JSON.stringify(foo, undefined, ' '); console.log(foo); 
 #disp { white-space: pre; font-family: monospace } 
 <div id="disp"></div> 
+127
javascript jquery object filtering
Feb 21 2018-11-21T00:
source share
11 answers

Never extend Object.prototype .

You will have a terrible business with your code. Everything will break. You extend all types of objects, including object literals.

Here is an example you can try:

  // Extend Object.prototype Object.prototype.extended = "I'm everywhere!"; // See the result alert( {}.extended ); // "I'm everywhere!" alert( [].extended ); // "I'm everywhere!" alert( new Date().extended ); // "I'm everywhere!" alert( 3..extended ); // "I'm everywhere!" alert( true.extended ); // "I'm everywhere!" alert( "here?".extended ); // "I'm everywhere!" 



Instead, create a function that passes the object.

 Object.filter = function( obj, predicate) { var result = {}, key; // ---------------^---- as noted by @CMS, // always declare variables with the "var" keyword for (key in obj) { if (obj.hasOwnProperty(key) && !predicate(obj[key])) { result[key] = obj[key]; } } return result; }; 
+160
Feb 21 2018-11-21T00:
source share

First of all, it is considered bad practice to extend Object.prototype . Instead, provide your function as a utility function for Object , just like there are already Object.keys , Object.assign , Object.is , ... etc.

I will give a few solutions here:

  1. Using reduce and Object.keys
  2. Like (1), combined with Object.assign
  3. Using map and distributing syntax instead of reduce
  4. Using Object.entries and Object.fromEntries

1. Using reduce and Object.keys

With reduce and Object.keys implement the required filter (using ES6 arrow syntax ):

 Object.filter = (obj, predicate) => Object.keys(obj) .filter( key => predicate(obj[key]) ) .reduce( (res, key) => (res[key] = obj[key], res), {} ); // Example use: var scores = { John: 2, Sarah: 3, Janet: 1 }; var filtered = Object.filter(scores, score => score > 1); console.log(filtered); 

Note that in the code above, predicate must be an inclusion condition (contrary to the exception condition used by the OP) so that it matches Array.prototype.filter by Array.prototype.filter .

2. Like (1), combined with Object.assign

In the above solution, the comma operator is used in the reduce part to return the mutated res object. Of course, this can be written as two statements instead of a single expression, but the latter is more concise. To do this without a comma operator, you can instead use Object.assign , which returns a mutated object:

 Object.filter = (obj, predicate) => Object.keys(obj) .filter( key => predicate(obj[key]) ) .reduce( (res, key) => Object.assign(res, { [key]: obj[key] }), {} ); // Example use: var scores = { John: 2, Sarah: 3, Janet: 1 }; var filtered = Object.filter(scores, score => score > 1); console.log(filtered); 

3. Using map and syntax propagation instead of reduce

Here we move the call to Object.assign from the loop so that it is executed only once, and pass it the individual keys as separate arguments (using the extension syntax ):

 Object.filter = (obj, predicate) => Object.assign(...Object.keys(obj) .filter( key => predicate(obj[key]) ) .map( key => ({ [key]: obj[key] }) ) ); // Example use: var scores = { John: 2, Sarah: 3, Janet: 1 }; var filtered = Object.filter(scores, score => score > 1); console.log(filtered); 

4. Using Object.entries and Object.fromEntries

Since the solution translates the object into an intermediate array, and then converts it back to a simple object, it would be useful to use Object.entries (ES2017) and use a method that does the opposite (i.e. creates an object from an array of key / value pairs ). At the time of writing Object.fromEntries proposal in step 3. Firefox and, more recently, Chrome have implemented it . Otherwise, you can use polyfill.

This leads to these two β€œsingle-line” methods on Object (including polyfill):

 Object.fromEntries = arr => Object.assign({}, ...arr.map( ([k, v]) => ({[k]: v}) )); Object.filter = (obj, predicate) => Object.fromEntries(Object.entries(obj).filter(predicate)); // Example use: var scores = { John: 2, Sarah: 3, Janet: 1 }; var filtered = Object.filter(scores, ([name, score]) => score > 1); console.log(filtered); 

The predicate function here receives a key / value pair as an argument, which is slightly different, but allows more possibilities in the logic of the predicate function.

+204
Jun 03 '16 at 13:46 on
source share

If you want to use underscore or lodash, you can use pick (or its opposite, omit ).

Examples from underscore documents:

 _.pick({name: 'moe', age: 50, userid: 'moe1'}, 'name', 'age'); // {name: 'moe', age: 50} 

Or with a callback (for lodash, use pickBy ):

 _.pick({name: 'moe', age: 50, userid: 'moe1'}, function(value, key, object) { return _.isNumber(value); }); // {age: 50} 
+19
05 Oct '16 at 11:40
source share

As Patrick already said, this is a bad idea, as it will almost certainly violate any third-party code that you will ever want to use.

All libraries, such as jquery or prototype, will be broken if you continue Object.prototype , because this lazy iteration over objects (without hasOwnProperty checks) will break, because the functions you add will be part of the iteration.

+5
Feb 21 '11 at 22:47
source share

ES6 approach ...

Imagine you have this object below:

 const developers = { 1: { id: 1, name: "Brendan", family: "Eich" }, 2: { id: 2, name: "John", family: "Resig" }, 3: { id: 3, name: "Alireza", family: "Dezfoolian" } }; 

Create function:

 const filterObject = (obj, filter, filterValue) => Object.keys(obj).reduce((acc, val) => (obj[val][filter] === filterValue ? acc : { ...acc, [val]: obj[val] } ), {}); 

And call it:

 filterObject(developers, "name", "Alireza"); 

and will return :

 { 1: { id: 1, name: "Brendan", family: "Eich" }, 2: { id: 2, name: "John", family: "Resig" } } 
+4
Feb 04 '19 at 7:21
source share

I created Object.filter() which not only filters by function, but also accepts an array of keys to include. An optional third parameter allows you to invert the filter.

Given:

 var foo = { x: 1, y: 0, z: -1, a: 'Hello', b: 'World' } 

Array:

 Object.filter(foo, ['z', 'a', 'b'], true); 

Function:

 Object.filter(foo, function (key, value) { return Ext.isString(value); }); 

The code

Disclaimer : I decided to use the Ext JS core for brevity. I did not feel the need to write type controllers for object types, since this was not part of the question.

 // Helper function function print(obj) { document.getElementById('disp').innerHTML += JSON.stringify(obj, undefined, ' ') + '<br />'; console.log(obj); } Object.filter = function (obj, ignore, invert) { let result = {}; // Returns a filtered copy of the original list if (ignore === undefined) { return obj; } invert = invert || false; let not = function(condition, yes) { return yes ? !condition : condition; }; let isArray = Ext.isArray(ignore); for (var key in obj) { if (obj.hasOwnProperty(key) && !(isArray && not(!Ext.Array.contains(ignore, key), invert)) && !(!isArray && not(!ignore.call(undefined, key, obj[key]), invert))) { result[key] = obj[key]; } } return result; }; let foo = { x: 1, y: 0, z: -1, a: 'Hello', b: 'World' }; print(Object.filter(foo, ['z', 'a', 'b'], true)); print(Object.filter(foo, (key, value) => Ext.isString(value))); 
 #disp { white-space: pre; font-family: monospace } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/extjs/4.2.1/builds/ext-core.min.js"></script> <div id="disp"></div> 
+3
Dec 31 '15 at 17:31
source share

What about:

 function filterObj(keys, obj) { const newObj = {}; for (let key in obj) { if (keys.includes(key)) { newObj[key] = obj[key]; } } return newObj; } 

Or...

 function filterObj(keys, obj) { const newObj = {}; Object.keys(obj).forEach(key => { if (keys.includes(key)) { newObj[key] = obj[key]; } }); return newObj; } 
+3
May 20 '17 at 17:33
source share

Given

 object = {firstname: 'abd', lastname:'tm', age:16, school:'insat'}; keys = ['firstname', 'age']; 

then:

 keys.reduce((result, key) => ({ ...result, [key]: object[key] }), {}); // {firstname:'abd', age: 16} 

 // Helper function filter(object, ...keys) { return keys.reduce((result, key) => ({ ...result, [key]: object[key] }), {}); }; //Example const person = {firstname: 'abd', lastname:'tm', age:16, school:'insat'}; // Expected to pick only firstname and age keys console.log( filter(person, 'firstname', 'age') ) 
+2
Nov 12 '16 at 11:17
source share

My confident solution:

 function objFilter(obj, filter, nonstrict){ r = {} if (!filter) return {} if (typeof filter == 'string') return {[filter]: obj[filter]} for (p in obj) { if (typeof filter == 'object' && nonstrict && obj[p] == filter[p]) r[p] = obj[p] else if (typeof filter == 'object' && !nonstrict && obj[p] === filter[p]) r[p] = obj[p] else if (typeof filter == 'function'){ if (filter(obj[p],p,obj)) r[p] = obj[p]} else if (filter.length && filter.includes(p)) r[p] = obj[p] } return r } 

Test cases:

 obj = {a:1, b:2, c:3} objFilter(obj, 'a') // returns: {a: 1} objFilter(obj, ['a','b']) // returns: {a: 1, b: 2} objFilter(obj, {a:1}) // returns: {a: 1} objFilter(obj, {'a':'1'}, true) // returns: {a: 1} objFilter(obj, (v,k,o) => v%2===1) // returns: {a: 1, c: 3} 

https://gist.github.com/khullah/872d5a174108823159d845cc5baba337

0
Dec 13 '18 at 18:22
source share

As everyone said, don't screw up with the prototype. Instead, just write a function for this. Here is my version with lodash :

 import each from 'lodash/each'; import get from 'lodash/get'; const myFilteredResults = results => { const filteredResults = []; each(results, obj => { // filter by whatever logic you want. // sample example const someBoolean = get(obj, 'some_boolean', ''); if (someBoolean) { filteredResults.push(obj); } }); return filteredResults; }; 
0
Jun 24 '19 at 5:07
source share

In these cases, I use jquery $ .map, which can handle objects. As mentioned in other answers, it is not recommended to change your own prototypes ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#Bad_practice_Extension_of_native_prototypes )

The following is an example of filtering by simply checking some properties of your object. It returns its own object if your condition is true, or returns undefined if not. The undefined property will make this entry disappear from your list of objects;

 $.map(yourObject, (el, index)=>{ return el.yourProperty ? el : undefined; }); 
-one
Sep 04 '18 at 7:47
source share



All Articles