JavaScript: filter() for Objects

JavascriptJqueryObjectFiltering

Javascript Problem Overview


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.

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>

Javascript Solutions


Solution 1 - Javascript

First of all, it's considered bad practice to extend Object.prototype. Instead, provide your feature as stand-alone function, or if you really want to extend a global, provide it 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):

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

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

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

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:

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 gets a key/value pair as argument here, which is a bit different, but allows for more possibilities in the predicate function's logic.

Solution 2 - Javascript

Never ever extend Object.prototype.

Horrible things will happen to your code. Things will break. You're extending all object types, including object literals.

Here's a quick 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 you pass the object.

Object.filter = function( obj, predicate) {
    let result = {}, key;

    for (key in obj) {
        if (obj.hasOwnProperty(key) && !predicate(obj[key])) {
            result[key] = obj[key];
        }
    }

    return result;
};

Solution 3 - Javascript

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} 

Solution 4 - Javascript

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}

Solution 5 - Javascript

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

Solution 6 - Javascript

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.

Solution 7 - Javascript

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

Solution 8 - Javascript

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

Solution 9 - Javascript

Plain ES6:

var foo = {
    bar: "Yes"
};

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

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

Solution 10 - Javascript

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.

// 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>

Solution 11 - Javascript

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

Solution 12 - Javascript

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)

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

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

Solution 13 - Javascript

You could also do something like this where you are filtering on the entries to find the key provided and return the value

   let func = function(items){
      let val
      Object.entries(this.items).map(k => {
        if(k[0]===kind){
         val = k[1]
        }
      })
      return val
   }

Solution 14 - Javascript

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 15 - Javascript

    var foo = {
    bar: "Yes",
    pipe: "No"
};

const ret =  Object.entries(foo).filter(([key, value])=> value === 'Yes');

https://masteringjs.io/tutorials/fundamentals/filter-object

Solution 16 - Javascript

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

Solution 17 - Javascript

If you don't need the original object, this is a simple, very boring answer that doesn't waste memory:

const obj = {'a': 'want this', 'b': 'want this too', 'x': 'remove this'}
const keep = new Set(['a', 'b', 'c'])

function filterObject(obj, keep) {
  Object.keys(obj).forEach(key => {
    if (!keep.has(key)) {
      delete obj[key]
    }
  })
}

If you're only filtering a small number of objects, and your objects don't have many keys, you might not want to bother with constructing a Set, in which case use array.includes instead of set.has.

Solution 18 - Javascript

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

Solution 19 - Javascript

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAgileMeansDoAsLittleAsPossibleView Question on Stackoverflow
Solution 1 - JavascripttrincotView Answer on Stackoverflow
Solution 2 - Javascriptuser113716View Answer on Stackoverflow
Solution 3 - JavascriptQui-Gon JinnView Answer on Stackoverflow
Solution 4 - JavascriptBogdan DView Answer on Stackoverflow
Solution 5 - JavascriptAlirezaView Answer on Stackoverflow
Solution 6 - JavascriptMartin JespersenView Answer on Stackoverflow
Solution 7 - JavascriptAbdennour TOUMIView Answer on Stackoverflow
Solution 8 - JavascriptshaunwView Answer on Stackoverflow
Solution 9 - JavascriptfrancisView Answer on Stackoverflow
Solution 10 - JavascriptMr. PolywhirlView Answer on Stackoverflow
Solution 11 - JavascriptBernardo Dal CornoView Answer on Stackoverflow
Solution 12 - JavascriptExodus 4DView Answer on Stackoverflow
Solution 13 - JavascriptTroy B.View Answer on Stackoverflow
Solution 14 - JavascriptyairnizView Answer on Stackoverflow
Solution 15 - JavascriptMiguel PegueroView Answer on Stackoverflow
Solution 16 - Javascriptsaran3hView Answer on Stackoverflow
Solution 17 - JavascriptChrisView Answer on Stackoverflow
Solution 18 - JavascriptАннаView Answer on Stackoverflow
Solution 19 - JavascriptMarcel KohlsView Answer on Stackoverflow