Tengo la sensación de que este es un lado oscuro de Javascript que simplemente no conozco ni entiendo.
Estoy recuperando una matriz de objetos de una base de datos usando Sequelize. Es una matriz relativamente compleja, ya que contiene asociaciones. La matriz se ve así (está simplificada):
[ { name: '...', director: '...', genres: [ {id: 1, name: 'Mystery'}, {id: 2, name: 'Thriller'}, ], }, ... ] Quiero mapear esta matriz para cambiar el formato de los genres de esto:
genres: [ {id: 1, name: 'Mystery'}, {id: 2, name: 'Thriller'}, ],a esto:
genres: ['Mystery', 'Thriller'],Así que hice algo que creo que es bastante común en Javascript:
movies.map((movie) => { movie.genres = movie.genres.map(g => g.name); movie.actors = movie.actors.map(a => a.name); return movie; }); console.log(JSON.stringify(movies)); Sin embargo, una vez impresa en la consola, la matriz permanece completamente sin cambios. Para tener el efecto deseado en esta matriz, tuve que convertirla en una cadena de caracteres y volver a convertirla en una matriz antes de realizar las operaciones map() , así:
movies = JSON.parse(JSON.stringify(movies)); movies.map((movie) => { movie.genres = movie.genres.map(g => g.name); movie.actors = movie.actors.map(a => a.name); return movie; }); console.log(JSON.stringify(movies));¿Por qué funcionó? Supongo que hacer esta conversión de un lado a otro eliminó ciertos campos del objeto, pero no veo cómo esto podría haber tenido un efecto en la matriz.
EDITAR: usar otros métodos como Array.from o el operador de propagación ( movies = [...movies] ) no funciona.
No estoy familiarizado con Sequelize, pero mi mejor suposición es que cuando ejecuta JSON.parse y JSON.stringify , crea una copia del valor devuelto por Sequelize. Cuando crea una copia, su código "posee" el nuevo objeto, lo que permite que se apliquen los cambios.
Si su argumento optionsArray incluye la opción 'IMMUTABLE' , entonces está funcionando según lo diseñado.
Si desea verificarlo (no para resolver nada, sino solo para sentirse un poco más cuerdo), entonces probablemente pueda verificar la inmutabilidad al ver qué devuelve Object.getOwnPropertyDescriptor (películas, 'géneros').
En cuyo caso, sí, lo mejor es crear una copia del objeto. Si se adscribe a la programación funcional, es la forma "correcta" de hacerlo. Nunca cambiar objetos, sino devolver otros nuevos es una práctica fundamental de la programación funcional y evita categorías enteras de errores de programación.
sean-777 tiene razón, puede clonarlo usando varias bibliotecas diferentes, ostructuredClone() . En el caso de tu ejemplo, probablemente iría con
function getName({name}) { return name; } function mapName(a) { a.map(getName); } function convertMovie(movie) { return { ...movie, genres: mapName(movie.genres), actors: mapName(movie.actors) }; } function convertMovies(movies) { return movies.map(convertMovie) } const moviesConverted = convertMovies(movies); console.log(JSON.stringify(moviesConverted));Puede usar el métodostructuredClone() que crea una clonación profunda de un valor dado usando el algoritmo de clonación estructurada.
demostración :
const jsObj = [ { name: 'alpha', director: 'beta', genres: [ {id: 1, name: 'Mystery'}, {id: 2, name: 'Thriller'}, ], } ]; const clonedObj = structuredClone(jsObj); const modifiedObj = clonedObj.map(obj => { obj.genres = obj.genres.map(genreObj => genreObj.name) return obj; }); console.log(jsObj); console.log(modifiedObj);