[javascript] JavaScript: filter() for Objects

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

How would I implement a filter() for Objects in JavaScript?

Let's say I have this object:

var foo = {
    bar: "Yes"
};

And I want to write a filter() that works on Objects:

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 following 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.

_x000D_
_x000D_
Object.prototype.filter = function(predicate) {_x000D_
  var result = {};_x000D_
  for (key in this) {_x000D_
    if (this.hasOwnProperty(key) && !predicate(this[key])) {_x000D_
      console.log("copying");_x000D_
      result[key] = this[key];_x000D_
    }_x000D_
  }_x000D_
  return result;_x000D_
};_x000D_
_x000D_
var foo = {_x000D_
  bar: "Yes",_x000D_
  moo: undefined_x000D_
};_x000D_
_x000D_
foo = foo.filter(function(property) {_x000D_
  return typeof property === "undefined";_x000D_
});_x000D_
_x000D_
document.getElementById('disp').innerHTML = JSON.stringify(foo, undefined, '  ');_x000D_
console.log(foo);
_x000D_
#disp {_x000D_
  white-space: pre;_x000D_
  font-family: monospace_x000D_
}
_x000D_
<div id="disp"></div>
_x000D_
_x000D_
_x000D_

This question is related to javascript jquery object filtering

The answer is


I use this when I need it:

const filterObject = (obj, condition) => {
    const filteredObj = {};
    Object.keys(obj).map(key => {
      if (condition(key)) {
        dataFiltered[key] = obj[key];
      }
    });
  return filteredObj;
}

My opinionated 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/bernardoadc/872d5a174108823159d845cc5baba337


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

I provide here several solutions:

  1. Using reduce and Object.keys
  2. As (1), in combination with Object.assign
  3. Using map and spread syntax instead of reduce
  4. Using Object.entries and Object.fromEntries

1. Using reduce and Object.keys

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

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

Note that in the above code predicate must be an inclusion condition (contrary to the exclusion condition the OP used), so that it is in line with how Array.prototype.filter works.

2. As (1), in combination with Object.assign

In the above solution the comma operator is used in the reduce part to return the mutated res object. This could of course be written as two statements instead of one expression, but the latter is more concise. To do it without the comma operator, you could use Object.assign instead, which does return the mutated object:

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

3. Using map and spread syntax instead of reduce

Here we move the Object.assign call out of the loop, so it is only made once, and pass it the individual keys as separate arguments (using the spread syntax):

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

4. Using Object.entries and Object.fromEntries

As the solution translates the object to an intermediate array and then converts that back to a plain object, it would be useful to make use of Object.entries (ES2017) and the opposite (i.e. create an object from an array of key/value pairs) with Object.fromEntries (ES2019).

It leads to this "one-liner" method on Object:

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

The predicate function gets a key/value pair as argument here, which is a bit different, but allows for more possibilities in the predicate function's logic.


I have created an Object.filter() which does not only filter by a function, but also accepts an array of keys to include. The optional third parameter will allow 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);
});

Code

Disclaimer: I chose to use Ext JS core for brevity. Did not feel it was necessary to write type checkers for object types as it was not part of the question.

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


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}

_x000D_
_x000D_
// Helper_x000D_
function filter(object, ...keys) {_x000D_
  return keys.reduce((result, key) => ({ ...result, [key]: object[key] }), {});_x000D_
  _x000D_
};_x000D_
_x000D_
//Example_x000D_
const person = {firstname: 'abd', lastname:'tm', age:16, school:'insat'};_x000D_
_x000D_
// Expected to pick only firstname and age keys_x000D_
console.log(_x000D_
  filter(person, 'firstname', 'age')_x000D_
)
_x000D_
_x000D_
_x000D_


In these cases I use the jquery $.map, which can handle objects. As mentioned on other answers, it's not a good practice to change native prototypes (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#Bad_practice_Extension_of_native_prototypes)

Below is an example of filtering just by checking some property of your object. It returns the own object if your condition is true or returns undefined if not. The undefined property will make that record disappear from your object list;

$.map(yourObject, (el, index)=>{
    return el.yourProperty ? el : undefined;
});

Plain ES6:

var foo = {
    bar: "Yes"
};

const res = Object.keys(foo).filter(i => foo[i] === 'Yes')

console.log(res)
// ["bar"]

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

Examples from underscore's docs:

_.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}

If you have Symbol properties in your object, that should be filtered too, you can not use: Object.keys Object.entries Object.fromEntries, ... because:

Symbol keys are not enumerable !

You could use Reflect.ownKeys and filter keys in reduce

Reflect.ownKeys(o).reduce((a, k) => allow.includes(k) && {...a, [k]: o[k]} || a, {});

(Open DevTools for log output - Symbols are not logged on Stackoverflow UI)

_x000D_
_x000D_
const bKey = Symbol('b_k');
const o = {
    a:                 1,
    [bKey]:            'b',
    c:                 [1, 3],
    [Symbol.for('d')]: 'd'
};

const allow = ['a', bKey, Symbol.for('d')];

const z1 = Reflect.ownKeys(o).reduce((a, k) => allow.includes(k) && {...a, [k]: o[k]} || a, {});

console.log(z1);                   // {a: 1, Symbol(b_k): "b", Symbol(d): "d"}
console.log(bKey in z1)            // true
console.log(Symbol.for('d') in z1) // true
_x000D_
_x000D_
_x000D_

This is equal to this

const z2 = Reflect.ownKeys(o).reduce((a, k) => allow.includes(k) && Object.assign(a, {[k]: o[k]}) || a, {});
const z3 = Reflect.ownKeys(o).reduce((a, k) => allow.includes(k) && Object.defineProperty(a, k, {value: o[k]}) || a, {});

console.log(z2); // {a: 1, Symbol(b_k): "b", Symbol(d): "d"}
console.log(z3); // {a: 1, Symbol(b_k): "b", Symbol(d): "d"}

Wrapped in a filter() function, an optional target object could be passed

const filter = (o, allow, t = {}) => Reflect.ownKeys(o).reduce(
    (a, k) => allow.includes(k) && {...a, [k]: o[k]} || a, 
    t
);

console.log(filter(o, allow));           // {a: 1, Symbol(b_k): "b", Symbol(d): "d"}
console.log(filter(o, allow, {e: 'e'})); // {a: 1, e: "e", Symbol(b_k): "b", Symbol(d): "d"}

If you wish to mutate the same object rather than create a new one.

The following example will delete all 0 or empty values:

const sev = { a: 1, b: 0, c: 3 };
const deleteKeysBy = (obj, predicate) =>
  Object.keys(obj)
    .forEach( (key) => {
      if (predicate(obj[key])) {
        delete(obj[key]);
      }
    });

deleteKeysBy(sev, val => !val);

Solution in Vanilla JS from year 2020.


let romNumbers={'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000}

You can filter romNumbers object by key:

const filteredByKey = Object.fromEntries(
    Object.entries(romNumbers).filter(([key, value]) => key === 'I') )
// filteredByKey = {I: 1} 

Or filter romNumbers object by value:

 const filteredByValue = Object.fromEntries(
    Object.entries(romNumbers).filter(([key, value]) => value === 5) )
 // filteredByValue = {V: 5} 

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 a 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"
  }
}

Like everyone said, do not screw around with prototype. Instead, simply write a function to do so. 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;
};

As patrick already stated this is a bad idea, as it will almost certainly break any 3rd party code you could ever wish to use.

All libraries like jquery or prototype will break if you extend Object.prototype, the reason being that lazy iteration over objects (without hasOwnProperty checks) will break since the functions you add will be part of the iteration.


How 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;
}

Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to jquery

How to make a variable accessible outside a function? Jquery assiging class to th in a table Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Getting all files in directory with ajax Bootstrap 4 multiselect dropdown Cross-Origin Read Blocking (CORB) bootstrap 4 file input doesn't show the file name Jquery AJAX: No 'Access-Control-Allow-Origin' header is present on the requested resource how to remove json object key and value.?

Examples related to object

How to update an "array of objects" with Firestore? how to remove json object key and value.? Cast object to interface in TypeScript Angular 4 default radio button checked by default How to use Object.values with typescript? How to map an array of objects in React How to group an array of objects by key push object into array Add property to an array of objects access key and value of object using *ngFor

Examples related to filtering

Truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all() Filtering array of objects with lodash based on property value How can I return the difference between two lists? I have filtered my Excel data and now I want to number the rows. How do I do that? Creating lowpass filter in SciPy - understanding methods and units filter items in a python dictionary where keys contain a specific string Detect and exclude outliers in Pandas data frame Filtering Pandas DataFrames on dates Logical operators for boolean indexing in Pandas How to run a SQL query on an Excel table?