[removed] filter() for Objects

前端 未结 16 807
心在旅途
心在旅途 2020-11-22 15:23

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

How would I implemen

相关标签:
16条回答
  • 2020-11-22 15:40

    How to search an array of objects using JavaScript: filter() for Objects

    let items = [{
        id: 1,
        isActive: true,
        age: 40,
        first_name: 'Dickerson',
        last_name: 'Macdonald'
      },
      {
        id: 2,
        isActive: false,
        age: 21,
        first_name: 'Larsen',
        last_name: 'Shaw'
      },
      {
        id: 3,
        isActive: false,
        age: 89,
        first_name: 'Geneva',
        last_name: 'Wilson'
      },
      {
        id: 4,
        isActive: true,
        age: 38,
        first_name: 'Mac',
        last_name: 'Henry'
      }
    ];
    
    function search(val) {
      let searchedItems = items.filter((item) => {
        return Object.keys(item).some((key) => {
          return String(item[key]).toLowerCase().indexOf(val.toLowerCase()) > -1;
        })
      });
    
      return searchedItems;
    }
    
    console.log(search("mac"))

    0 讨论(0)
  • 2020-11-22 15:42

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

    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.

    0 讨论(0)
  • 2020-11-22 15:42

    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}
    
    0 讨论(0)
  • 2020-11-22 15:44

    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"
      }
    }
    
    0 讨论(0)
  • 2020-11-22 15:45

    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;
    };
    
    0 讨论(0)
  • 2020-11-22 15:46

    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;
    };
    
    0 讨论(0)
提交回复
热议问题