objetos - reduce javascript
JavaScript: filter() para Objetos (7)
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 prototype se romperán si extiende Object.prototype
, ya que la iteración diferida sobre los objetos (sin hasOwnProperty
comprobaciones hasOwnProperty
) se romperá ya que las funciones que agregue serán parte de la iteración.
ECMAScript 5 tiene el prototipo filter()
para tipos de Array
, pero no tipos de Object
, si entiendo correctamente.
¿Cómo implementaría un filter()
para Object
s en JavaScript?
Digamos que tengo este objeto:
var foo = {
bar: "Yes"
};
Y quiero escribir un filter()
que funcione en 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;
};
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>
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'')
)
En primer lugar, se considera una mala práctica extender Object.prototype
. En su lugar, proporcione su función como función de utilidad en Object
, al igual que ya existen Object.keys
, Object.assign
, Object.is
, ... etc.
Puede usar reduce
y Object.keys
para implementar el filtro deseado (usando la sintaxis de flecha ES6):
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 el predicate
debe ser una condición de inclusión (a diferencia de la condición de exclusión que utilizó el OP), de modo que esté en línea con la forma en que funciona Array.prototype.filter
.
Con Object.assign
En la solución anterior, el operador de coma se usa en la parte de reduce
para devolver el objeto res
mutado. 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ía usar Object.assign
en Object.assign
lugar, que devuelve 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 la llamada Object.assign
del bucle, por lo que solo se realiza una vez, y le pasamos las claves individuales como argumentos separados (usando la 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: Object.from
personalizado
Como la solución traduce el objeto a una matriz intermedia y luego lo convierte de nuevo en un objeto simple, sería útil hacer uso de Object.entries
(ES2017) y crear un método que haga lo contrario (es decir, crear un objeto desde un matriz de pares clave / valor ).
Conduce a estos dos métodos de "un solo liner" 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.
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
Descargo de responsabilidad : Elegí usar el núcleo Ext JS para abreviar. No pensé que era necesario escribir verificadores de tipos de objetos porque no era parte de la pregunta.
// Helper functions
function clone(obj) { return JSON.parse(JSON.stringify(obj)); }
function print(obj) {
document.getElementById(''disp'').innerHTML += JSON.stringify(obj, undefined, '' '') + ''<br />'';
console.log(obj);
}
Object.filter = function (obj, ignore, invert) {
if (ignore === undefined) {
return obj;
}
invert = invert || false;
var not = function(condition, yes) { return yes ? !condition : condition; };
var 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))) {
delete obj[key];
}
}
return obj;
};
var foo = {
x: 1,
y: 0,
z: -1,
a: ''Hello'',
b: ''World''
}, bar = clone(foo);
print(Object.filter(foo, [''z'', ''a'', ''b''], true));
print(Object.filter(bar, function (key, value) {
return 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>
Nunca extienda Object.prototype
.
Cosas horribles pasarán con tu código. Las cosas se romperán. Extiende todos los tipos de objeto, incluidos los 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;
};
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;
}
Si está dispuesto a utilizar guiones bajos o lodash , puede 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 , use pickBy ):
_.pick({name: ''moe'', age: 50, userid: ''moe1''}, function(value, key, object) {
return _.isNumber(value);
});
// {age: 50}