I have the feeling that this is an obscure side of Javascript that I simply do not know or understand.
I'm retrieving an array of objects from a database using Sequelize. It's a relatively complex array, as in contains associations. The array looks like this (it is simplified) :
[
{
name: '...',
director: '...',
genres: [
{id: 1, name: 'Mystery'},
{id: 2, name: 'Thriller'},
],
},
...
]
I want to map this array so to change the format of genres
from this:
genres: [
{id: 1, name: 'Mystery'},
{id: 2, name: 'Thriller'},
],
to this:
genres: ['Mystery', 'Thriller'],
So I did something which I believe is quite common in 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));
However, once printed to the console, the array is completely unchanged. To have the desired effect on this array, I had to convert it into a string of characters and back into an array before performing the map()
operations, like so:
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));
Why did it work ? I'm guessing that doing this conversion back and forth removed certain fields from the object, but I do not see how this could've had an effect on the array.
EDIT: using other methods like Array.from
or the spread operator (movies = [...movies]
) does not work.
CodePudding user response:
I am not familiar with Sequelize, but my best guess is that when you run JSON.parse
and JSON.stringify
, you create a copy of the value returned from Sequelize. When you create a copy, your code then "owns" the new object, thus allowing changes to be applied.
CodePudding user response:
If your optionsArray
argument includes the 'IMMUTABLE'
option then it's working as designed.
If you want to check it (not to solve anything but just to feel a little more sane) then you can probably verify immutability by seeing what Object.getOwnPropertyDescriptor(movies, 'genres') returns.
In which case, yes, creating a copy of the object is best. If you ascribe to functional programming, it's the "right" way to do it. Never changing objects but instead returning new ones is a fundamental practice of functional programming and avoids entire categories of programming errors.
sean-777 is right, you can clone it using several different libraries, or structuredClone(). In the case of your example, I'd probably just go with
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)
}
moviesConverted = convertMovies(movies);
console.log(JSON.stringify(moviesConverted));
CodePudding user response:
You can use structuredClone() method which creates a deep clone of a given value using the structured clone algorithm.
Demo :
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);