Pregunta JavaScript: filter () para Objetos


ECMAScript 5 tiene el filter() prototipo para Array tipos, pero no Object tipos, si entiendo correctamente.

¿Cómo implementaría un filter() para Objects en JavaScript?

Digamos que tengo este objeto:

var foo = {
    bar: "Yes"
};

Y quiero escribir un filter() eso funciona 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;
};

Esto funciona cuando lo uso en la siguiente demostración, pero cuando lo agrego a mi sitio que usa jQuery 1.5 y jQuery UI 1.8.9, obtengo errores de JavaScript en 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>


75
2018-02-21 22:42


origen


Respuestas:


Nunca extender Object.prototype.

Cosas horribles pasarán con tu código. Las cosas se romperán. Estás extendiendo todas tipos de objeto, incluidos literales de objeto.

Aquí hay un ejemplo rápido que puedes probar:

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

En lugar de crear una función que pase el objeto.

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

111
2018-02-21 22:43



Ante todo, se considera una mala práctica extender Object.prototype. En cambio, proporcione su función como función de utilidad en Object, al igual que ya hay Object.keys, Object.assign, Object.is, ... etc.

Puedes usar reduce y Object.keys para implementar el filtro deseado (usando ES6 sintaxis de flecha)

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

Tenga en cuenta que en el código anterior predicate debe ser un inclusión condición (contraria a la exclusión condición del OP utilizado), para que esté en línea con la forma Array.prototype.filter trabajos.

Con Object.assign

En la solución anterior, operador de coma se usa en el reduce parte para devolver el mutado res objeto. Por supuesto, esto podría escribirse como dos declaraciones en lugar de una expresión, pero la última es más concisa. Para hacerlo sin el operador de coma, podrías usar Object.assign en cambio, que hace devolver el objeto mutado:

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

Usar la sintaxis extendida

Aquí movemos el Object.assign llamar fuera del ciclo, por lo que solo se hace una vez, y pasarle las claves individuales como argumentos separados (usando el sintaxis extendida)

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

Funcionalidad de división: personalizada Object.from

A medida que la solución traduce el objeto a una matriz intermedia y luego lo convierte en un objeto simple, sería útil hacer uso de Object.entries (ES2017) y para crear un método que haga lo contrario (es decir crear un objeto a partir de una matriz de pares clave / valor)

Conduce a estos dos métodos de "una sola línea" en Object:

Object.from = arr => Object.assign(...arr.map( ([k, v]) => ({[k]: v}) ));
Object.filter = (obj, predicate) => Object.from(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);

La función de predicado obtiene un par de clave / valor como argumento aquí, que es un poco diferente, pero permite más posibilidades en la lógica de la función de predicado.


123
2018-06-03 13:46



Si estás dispuesto a usar guion bajo o lodash, puedes usar pick (o su opuesto, omit)

Ejemplos de documentos de subrayado:

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

O con una devolución de llamada (para lodash, utilizar pickBy)

_.pick({name: 'moe', age: 50, userid: 'moe1'}, function(value, key, object) {
  return _.isNumber(value);
});
// {age: 50}

14
2017-10-05 11:40



Como ya dijo patrick, esta es una mala idea, ya que es casi seguro que romperá cualquier código de terceros que puedas desear usar.

Todas las bibliotecas como jquery o prototipo se romperán si extiende Object.prototype, la razón es esa iteración perezosa sobre los objetos (sin hasOwnProperty cheques) se romperá ya que las funciones que agrega serán parte de la iteración.


2
2018-02-21 22:47



He creado un Object.filter() que no solo filtra por una función, sino que también acepta una matriz de claves para incluir. El tercer parámetro opcional le permitirá invertir el filtro.

Dado:

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

Formación:

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

Función:

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

Código

Renuncia: Elegí usar Ext JS core para abreviar. No pensé que era necesario escribir verificadores de tipos de objetos porque no era parte de la pregunta.


1
2017-12-31 17:31



Qué tal si:

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

O...

function filterObj(keys, obj) {
  const newObj = {};
  Object.keys(obj).forEach(key => {
    if (keys.includes(key)) {
      newObj[key] = obj[key];
    }
  });
  return newObj;
}

0
2018-05-20 17:33



Dado

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

keys = ['firstname', 'age'];

entonces :

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


0
2017-11-12 11:17