Espero que alguien pueda ayudarme con mi problema! No encontré lo correcto a través de la búsqueda y tal vez alguien pueda darme una pista.
Estoy llamando a una API que devuelve un objeto que a su vez contiene objetos anidados. En estos objetos anidados hay dos propiedades "valor" y "escala". Quiero dividir estas dos propiedades y escribirlas como propiedades en el mismo objeto.
Los datos que obtengo de la API son dinámicos, lo que significa que cambian constantemente.
Ejemplo:
// call api const apiCall = callApi(...); // return object console.log(apiCall); { id: '3454353458764389759834534534', json_data: { persons: { de: { name: 'Bob', data: { scale: 100, value: 2459, }, }, be: { name: 'Alice', data: { scale: 10000, value: 1459, }, }, }, url: 'https://stackoverflow.com/', timestamp: '2021-10-23T12:00:11+00:00', disclaimer: 'Some bla bla', }, } // targed object const objTarged = { id: '3454353458764389759834534534', json_data: { persons: { de: { name: 'Bob', data: { scale: 100, value: 2459, result: 24.59 // value / scale = result }, }, be: { name: 'Alice', data: { scale: 10000, value: 1459, result: 0.1459 // value / scale = result }, }, }, url: 'https://stackoverflow.com/', timestamp: '2021-10-23T12:00:11+00:00', disclaimer: 'Some bla bla', }, };Mis pensamientos:
Muchas gracias de antemano :)
Crearía una función mapValues() que toma un objeto y crea un nuevo objeto pasando cada uno de los valores del objeto en una función de transformación.
Cada vez que la llamada a la API devuelve un nuevo objeto, recreamos el nuevo objeto con la propiedad de result de acuerdo con la estructura.
¿Cómo funciona la función mapValues ?
Cada vez que se pasa un objeto (o matriz) a mapValues , se convierte en una matriz de pares [key, value] . Luego, los pares se asignan a nuevas entradas [key, pair] aplicando transformFn al valor. La matriz de transformación de pares se vuelve a convertir en un objeto usando Object.fromEntries() .
const mapValues = (transformFn, obj) => Object.fromEntries( Object.entries(obj) .map(([key, value]) => [key, transformFn(value)]) ) const apiCall = {"persons":{"de":{"name":"Bob","scale":100,"value":2459},"be":{"name":"Alice","scale":10000,"value":1459}}} const result = mapValues( val => mapValues(v => ({ ...v, result: v.value / v.scale, }), val), apiCall ) console.log(result) Si tiene varios niveles anidados con propiedades que no desea transformar, también podemos pasar la key a transformFn para un cambio más granular. Ahora podemos crear una función recursiva para atravesar el árbol y solo actualizar los objetos que tienen una clave específica.
const mapValues = (transformFn, obj) => Object.fromEntries( Object.entries(obj) .map(([key, value]) => [key, transformFn(value, key)]) ) const fn = obj => mapValues( (val, key) => { // if the key is data create a new object with a result property if(key === 'data') return ({ ...val, result: val.value / val.scale, }) // if it's object pass it to the recursive function if(typeof val === 'object') return fn(val) return val }, obj ) const apiCall = {"id":"3454353458764389759834534534","json_data":{"persons":{"de":{"name":"Bob","data":{"scale":100,"value":2459}},"be":{"name":"Alice","data":{"scale":10000,"value":1459}}},"url":"https://stackoverflow.com/","timestamp":"2021-10-23T12:00:11+00:00","disclaimer":"Some bla bla"}} const result = fn(apiCall) console.log(result)Puede ser útil descomponer el problema para encontrar primero los objetos anidados con las claves que le interesan. Una vez hecho esto, será fácil aumentar esos objetos con el cálculo deseado.
A continuación se muestra una especie de función genérica que encuentra un objeto anidado en función de que tenga una clave particular. Con eso, fixMyApiData se escribe solo...
// return an array of objects that are nested in the passed object which contain the passed key function objectsContainingKey(object, key) { let results = []; Object.keys(object).forEach(k => { if (k === key) results.push(object); if (object[k] && typeof object[k] === 'object') results = results.concat(objectsContainingKey(object[k], key)); }); return results; } // find the nested objects we care about and augment them with the value/scale calculation function fixMyApiData(apiData) { objectsContainingKey(apiData, 'scale').forEach(data => { if (data.value) data.result = data.value / data.scale; }) } let apiData = { id: '3454353458764389759834534534', json_data: { persons: { de: { name: 'Bob', data: { scale: 100, value: 2459, }, }, be: { name: 'Alice', data: { scale: 10000, value: 1459, }, }, }, url: 'https://stackoverflow.com/', timestamp: '2021-10-23T12:00:11+00:00', disclaimer: 'Some bla bla', }, }; fixMyApiData(apiData); console.log(apiData);