Tengo un objeto como este:
const obj = { name: "one", created: "2022-05-05T00:00:00.0Z", something: { here: "yes" } }Puedo verificar si tiene ciertos atributos uno por uno:
assert( obj.name !== undefined && obj.name === "one") assert( obj.something !== undefined && obj.something.here !== undefined && obj.something.here === "yes") ¿Hay alguna manera de lograr lo mismo pasando otro objeto para comparar? El otro objeto tendría menos valores (en este ejemplo no me importa si el atributo created está presente)
Algo como:
const obj = { name: "one", created: "2022-05-05T00:00:00.0Z", something: { here: "yes" } } hasAllOf(obj, { name: "one", something: { here: "yes" } }) // returns true¿Ya sea con lodash o alguna otra biblioteca?
Puede escribir hasAllOf recursivo con un asistente de verificación de tipos, is :
const is = (t, T) => t?.constructor === T const hasAllOf = (a, b) => is(a, Object) && is(b, Object) || is(a, Array) && is(b, Array) ? Object.keys(b).every(k => hasAllOf(a[k], b[k])) : a === b const obj = { name: "one", created: "2022-05-05T00:00:00.0Z", something: { here: "yes", and: ["this", 2, { f: "three" }] } } console.log( hasAllOf(obj, { name: "one", something: { here: "yes", and: ["this", 2, { f: "three" }] } }) ) Admite Map y Set mediante el uso de otro condicional
const is = (t, T) => t?.constructor === T const hasAllOf = (a, b) => { switch (true) { case is(a, Object) && is(b, Object) || is(a, Array) && is(b, Array): return Object.keys(b).every(k => hasAllOf(a[k], b[k])) case is(a, Set) && is(b, Set): return [...b].every(v => a.has(v)) case is(a, Map) && is(b, Map): return [...b.keys()].every(k => hasAllOf(a.get(k), b.get(k))) default: return a === b } } const obj = { name: new Map([[1, "one"], [2, "two"]]), created: "2022-05-05T00:00:00.0Z", something: { here: new Set("yes", "ok"), and: ["this", 2, { f: "three" }], } } console.log( hasAllOf(obj, { name: new Map([[1, "one"]]), something: { here: new Set("yes"), and: ["this", 2, { f: "three" }] } }) ) function hasAllOf(obj,data) { let obj_keys = Object.keys(obj) for (let key in data) { if (!(obj_keys.includes(key))) { return false; } if (typeof data[key] === "object" && typeof obj[key] === "object") { let res = hasAllOf(obj[key], data[key]) if (!res) { return res; } res = hasAllOf(data[key], obj[key]) if (!res) { return res; } } else if (data[key] !== obj[key]) { return false; } } return true; } const obj = { name: "one", created: "2022-05-05T00:00:00.0Z", something: { here: "yes", } } const data = { name: "one", something: { here: "yes", }, } console.log(hasAllOf(obj, data))Creo que _.conformsTo coincide de cerca con lo que estoy buscando, es un poco detallado, pero mucho mejor y legible que hacer todas las comparaciones una por una (especialmente en objetos más grandes)
_.conformsTo(obj, { name: (n) => n === "one", created: (c) => _.isString(c), // calling _conformsTo on nested objects something: (s) => _.conformsTo(s, { here: (h) => h === "yes" }) }) Incluso puedo crear una función is y conformsTo que devuelve una función "parcialmente aplicada" y la hace más legible.
Ejemplo completo:
const _ = require('lodash') const is = (expected) => (actual) => actual === expected const conformsTo = (template) => (actual) => _.conformsTo(actual, template) _.conformsTo( { // source name: "one", created: "2022-05-05T00:00:00.0Z", something: { here: "yes" } }, { // "spec" name: is('one'), something: conformsTo({ here: is("yes") }) } )