Typescript no actualiza elementos de un array y me tiene desesperado

D10X

Llevo unos dias volviendome loco, tengo este cacho de codigo, donde añado una nueva clave a un objeto en typescript.

 surveys.forEach((survey) => {
      this.logger.log(JSON.stringify(survey))
      Object.assign(survey, { userStatus: 'answered' })
      this.logger.log(JSON.stringify(survey))
    })

Y el log en ambos puntos es identico, ignorando el elemento q le he metido.

He probado de todo, haciendo un simple survey.userStatus='answered', pasandolo a un map, y nada. Sin embargo, si me deja cambiar keys q ya existieran previamente.

Al final mi solución ha sido la de rehacer por completo el array.

 
let newSurveys = []
surveys.forEach((survey) => {
      let tmpObj = JSON.parse(JSON.stringify(survey))
      this.logger.log(JSON.stringify(tmpObj))
      Object.assign(tmpObj, { userStatus: 'answered' })
      this.logger.log(JSON.stringify(tmpObj))
      newSurveys.push(tmpObj)
    })

Y asi funciona correctamente, pero quiero saber q es lo que pasa, q estoy haciendo mal o q se me escapa de JS para q no me altere la estructura del array.

Traber

Lo que obtienes en un foreach es una copia del item de dentro del array, pero no una referencia, así que lo que actualizas no es la copia que está dentro del array, sino la copia sobre la cual has iterado, el array como tal queda intacto.

Mírate esto:
https://stackoverflow.com/a/12482991

2 1 respuesta
Axtrix

Aqui lo tienes en una linea con el .map

const newArr = surveys.map(survey=> ({...survey, userStatus: 'answered'}));
1 3 respuestas
Traber

#3 Creo que no has leído bien la pregunta...

D10X

#2 Habia leido lo de las referencias, pero nada ... he intentado incluir los parametros q menciona en el foreach para no atacar a las referencias.

surveys.forEach(function (survey, index) {
      Object.assign(this[index], { userStatus: 'answered' })
    }, surveys)

Y aun asi, sigue sin modificar el array q devuelve.

No se si tendra algo q ver, pero esto es un javascript q se usa en el backend sobre un servidor node.

#3 Esto es un metodo GET de un REST q te devuelve las surveys, y surveys es un array que se rellena recuperando documentos de MongoDB,

Si lo devuelvo tal cual, me pinta unicamente los valores q yo he metido en cada survey.

Me resulta curioso, q si hago el {...survey, userStatus:'answered'] me devuelve un map de surveys con TODOS los parametros del documento, es decir, atributos internos de mongodb, y el userStatus q le acabo de poner.

Sin embargo, si hago un Object.assign(...) no me devuelve el userStatus, ni los atributos internos de mongo.

Si hago la ñapa del JSON.parse(JSON.stringify(...)) no me devuelve los atributor internos de mongo, pero si he conseguido lo q queria de un elemento userStatus con lo q quiero.

1 respuesta
Axtrix

#5 Estas haciendo el map en el front o en el backend? Mongoose no deberia devolverte los atributos internos de nada siempre que uses el uses .lean() para devolver un POJO en vez de un model de "mongoose" (o typegoose si estas usando TS).

https://mongoosejs.com/docs/tutorials/lean.html

2 respuestas
D10X

#6 Todo es backend es un API Rest picado en Express/Typescript ...

Voy a mirar lo del lean

Axtrix

Pruebalo y me dices, de todas formas, ¿Porque tienes que añadirle ese atributo despues de sacarlo de la base de datos? ¿No puede ser parte del modelo de mongoose o ser un virtual?

1 respuesta
HiGher

Object.assign no muta ninguno de sus argumentos, sino que devuelve un nuevo objeto con las transformaciones aplicadas. La respuesta de #3 debería resolver tu problema. Si no te gusta el spread:

let newSurveys = surveys.map((survey) => Object.assign(survey, { userStatus: 'answered' }));

.map itera y devuelve un nuevo array con las transformaciones realizadas en cada paso. La arrow function tiene return implícito cuando su body es de una línea, por lo que usará el valor devuelto por Object.assign, que es lo que te faltaba.

2 respuestas
isvidal

#9 const porfavor, el toc me mata

1 respuesta
Axtrix

#10

var newSurveys = surveys.map(function (survey) { return Object.assign(survey, { userStatus: 'answered' })});

Mejor?

/s

1
D10X

#8 Estoy devolviendo diferentes datos calculados, esto es una pequeña parte de la logica, y estoy poniendo a pincho eso simplemente para comprobar q metodo funciona.
#9 Eso lo he probado, pero se la suda.

Basicamente, mi metodo de prueba es esto:

async getSurveys(userId?) {
	let surveys = await this.surveyRepository.findAll()

//Inicio pruebas
	const newSurveys: ISurveyDTO[] = surveys.map((survey) =>
	  Object.assign(survey, { userStatus: 'answered' })
	)
//Fin pruebas
	return newSurveys
}

Y el resultado es;

a) const newSurveys: ISurveyDTO[] = surveys.map((survey) => Object.assign(survey, { userStatus: 'answered' })

newSurveys no contiene el elemento userStatus

b) const newSurveys: ISurveyDTO[] = surveys.map(survey=> ({...survey, userStatus: 'answered'}))

newSurveys contiene el elemento userStatus, y como decia #6 , al metodo find le he añadido el lean() y asi no me devuelve los elementos internos de mongo.



Me voy a quedar con la opcion b), q hace q funcione como yo "esperaba", pero como bien decis ... el Object.assign() deberia hacer lo mismo, y sin embargo ... no lo hace.

Muchas gracias! Al final he podido llegar a una solución decente.

1 respuesta
isvidal

#12 Esto no tiene ningún tipo de sentido, yo lo he probado en navegador y en node y la primera funciona perfectamente, o mas que perfectamente lo esperado, el array contiene la copia del objeto con la nueva propiedad asignada.

Algo raro esta pasando aqui.

1 respuesta
D10X

#13 Ja, dime a mi si es raro, q abri el hilo a las 3,30 de la mañana. Anda q no perdi tiempo con esta gilipollez

1
MTX_Anubis

Tiene pinta de que el survey ese es algún tipo de clase que tiene overrided algunos métodos por defecto que se utilizan para iterar sobre sus atributos que los tendrá dentro de _doc o similares y el Object.assign te lo asigna al root.

JuAn4k4

Buscar en SO, de primero de informático: https://stackoverflow.com/questions/34364015/object-assign-not-working-as-expected

Al larecer es por mongoose y lo que hace es esa clase que han hecho.

Yo transformaría siempre lo que sacas de la db (dto) a algo de tu domino hacia afuera, de forma que no trabajas con esos objetos de mongoose.

D10X

Me cago en la leche, pues mira q di vueltas por SO y vi un monton de ejemplos y tal, pero nada relacionado.

Eso debia ser justo lo q estaba pasando.

1 respuesta
JuAn4k4

#17 Lo he encontrado a la primera, buscando "Object.assign mongose node not working"

Por lo visto hay un .toObject(), miedo me da saber que carajo es, imagino que un proxy o un class. No quiero mirar.

Usuarios habituales

  • JuAn4k4
  • D10X
  • MTX_Anubis
  • isvidal
  • Axtrix
  • HiGher
  • Traber