In what way does the map assign the new value in this scenario?

I have an array named this.list and the goal is to iterate over its items and assign new values to them:

this.list = this.list.map(item => {
  if (item.id === target.id) {
    item.dataX = parseFloat(target.getAttribute('data-x'))
    item.dataY = parseFloat(target.getAttribute('data-y'))
  }
  return item
})

Surprisingly, I noticed that the following code also worked:

this.list.map(item => {
  if (item.id === target.id) {
    item.dataX = parseFloat(target.getAttribute('data-x'))
    item.dataY = parseFloat(target.getAttribute('data-y'))
  }
  return item
}) 

I'm puzzled as to why this.json is getting a new value from map even though there is no explicit assignment happening.

Answer №1

After reviewing the provided example, here are some comments for clarification:

// The function map creates a _new_ array
// with all existing elements.
// This new array is then assigned back to the original variable,
// effectively replacing the original array.
this.list = this.list.map(item => {

  // When referencing "item" here, it directly corresponds to the object element within
  // the current list. Any changes made to this object will affect the original item in the list.
  if (item.id === target.id) {

    // These lines represent your assignments.
    item.dataX = parseFloat(target.getAttribute('data-x'));
    item.dataY = parseFloat(target.getAttribute('data-y'));
  }

  // Despite returning the item which would replace the
  // existing element in the new list, the returned item itself
  // remains as the actual element already present in the list.
  return item;
});

So, in terms of outcome, the following code provides an equivalent functionality since you are updating the initial values within the original variable:

this.list.forEach(item => {
  if (item.id === target.id) {
    item.dataX = parseFloat(target.getAttribute('data-x'));
    item.dataY = parseFloat(target.getAttribute('data-y'));
  }
});

If you wish to perform this without mutating the original objects, you can consider the following approach:

var result = this.list.map(item => {
  return target.id === item.id ? {
    id: item.id,
    dataX: parseFloat(target.getAttribute('data-x')),
    dataY: parseFloat(target.getAttribute('data-y'))
  } : item;
});

In instances where 'item' carries additional data that needs to be preserved, you might need to clone the object. One methodology could be modeling the types accordingly, like shown below:

class Item {
  constructor(id, x, y) {
    this.id = id;
    this.dataX = dataX;
    this.dataY = dataY;
  }

  // Immutability style setter, returns the result
  // as a new instance.
  withData(dataX, dataY) {
    return new Item(this.id, dataX, dataY);
  }
}

var result = this.list.map(item => {
  return item.id === target.id ? item.withData(
    parseFloat(target.getAttribute('data-x')),
    parseFloat(target.getAttribute('data-y'))
  ) : item;
});

In the above scenario, this.list contains the untouched original array with all its elements prior to the mapping operation.

result consists of both 'updated' elements (new instances of elements that match target.id) and original items that do not.

Tracking Instances...

To illustrate this further, suppose we number all instances before the map operation using 'map' and assign it back to 'this.list':

this.list
  Array 1
  - Item 1
  - Item 2
  - Item 3
  - Item 4

Post-map operation, while the instances have been updated but remain the same, the array itself is now a different instance:

this.list -> map -> this.list
  Array 1             Array 2 (NEW)
  - Item 1       ->   - Item 1
  - Item 2       ->   - Item 2
  - Item 3       ->   - Item 3
  - Item 4       ->   - Item 4

In comparison to the forEach example, no change occurs because the instances are updated in place:

this.list (forEach) this.list (No change)
  Array 1             Array 1
  - Item 1            - Item 1
  - Item 2            - Item 2
  - Item 3            - Item 3
  - Item 4            - Item 4

In each of the immutable examples, though this.list retains its original form, 'result' becomes a distinct array instance with matched items being altered. Here, 'Item 1' was matched and updated:

this.list -> map -> result             this.list (Untouched)
  Array 1             Array 2 (NEW)      Array 1
  - Item 1            - Item 5 (NEW)     - Item 1
  - Item 2       ->   - Item 2           - Item 2
  - Item 3       ->   - Item 3           - Item 3
  - Item 4       ->   - Item 4           - Item 4

Answer №2

Regarding the documentation for the map function:

The map function does not change the original array it is called on (although the callback function, if used, might).

Keep in mind that the item variable you are working with in the callback function is a reference to the real this.list element and in this situation, you are altering the item object.

Answer №3

When working with JavaScript, it's important to remember that objects are stored as references. This means that when you iterate over an array and make changes to the objects within it, you are actually modifying the original object. The code snippet below illustrates this concept:

let original = [{x: 5}];
let copied = original;
copied[0].x = 6;
console.log(original[0].x); // 6

To avoid altering the original object, you will need to either recreate the object or clone it in some way.

this.list = this.list.map(item => {
    if (item.id === target.id) {
      item = {
        id: target.id,
        dataX: parseFloat(target.getAttribute('data-x')),
        dataY: parseFloat(target.getAttribute('data-y')),
        ...
      };
    }
    return item
})

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

NodeJS Streams: Delay in data transfer with Readable.pipe()

My understanding was that Stream.Readable.pipe() would immediately pipe data as it receives it, but I'm facing unexpected results while trying to create my own streams. const { Writable, Readable } = require("stream"); const writable = new ...

testing express router with several different handlers

I have been testing my guard middleware and everything appears to be functioning correctly, but my expect statement is failing. /// auth.test.js const request = require('supertest'); const express = require('express'); const app = req ...

ReactJS input range issue: Cannot preventDefault within a passive event listener invocation

I've been encountering some issues with the react-input-range component in my React App. It functions perfectly on larger viewports such as PCs and desktops, but on smaller devices like mobile phones and tablets, I'm seeing an error message "Unab ...

Tips for ensuring all data is downloaded in Firebase (Firestore) Storage before proceeding?

I am currently developing a light dashboard in Vue that connects to Firestore and Storage. As someone who is not an expert, I have encountered a roadblock in what should be a simple task. The issue lies with a function that is meant to retrieve all URLs ba ...

Developing components with jQuery

I have a JavaScript program that works perfectly when I use the following line of code: li = $("<li data-role='collapsible' data-iconpos='right' data-inset='false'></li>"); However, if I change the above line ...

Leverage information stored in an array within the HandsonTable Angular directive

Some of my columns in a HandsoneTable created using Angular directives are not rendering when I try to use an array as the data source with common array notation (name[0]). I'm unsure if this is supposed to work like this or if I am doing something wr ...

Using jQuery to import an external script into a React JS project

I'm looking to integrate an external JavaScript file (using jQuery) into a ReactJS project. While I found some guidance on this page, I am still encountering errors. The external JS file is named external.js: $(document).ready(function() { docu ...

Challenges with Tab navigation in React and Ionic 5

I am facing a challenge with getting the tabs navigation to function correctly. Here is my current code: App.tsx: const App: React.FC = () => <IonApp> <IonReactRouter> <IonRouterOutlet id="main"> < ...

The JQuery JavaScript function fails to complete its execution

Question: How can I resolve the issue where my PHP file returns a large JSON string with approximately 2000 elements, each having 14 child elements? When using jQuery AJAX to fetch the JSON and populate an ID-identified table, the filling process stops mid ...

"Exploring the Power of ZF2 with Restful APIs and Image

I am currently in the process of developing a website utilizing Zend Framework 2 in combination with AngularJS. The backend consists of a restful webservice running on ZF2, while AngularJS is used on the client side to interact with this webservice. My ne ...

A guide on iterating through an array in vue.js and appending a new attribute to each object

To incorporate a new property or array item into an existing virtual DOM element in Vue.js, the $set function must be utilized. Attempting to do so directly can cause issues: For objects: this.myObject.newProperty = "value"; For arrays: ...

Toggle the visibility of dropdown list items in different ways: Add or Remove, or Show or

Currently, I am working on a project that involves 3 drop down lists for security questions. I have implemented javascript functionality that triggers an alert when a selection is made in any of the drop down boxes. My challenge now is figuring out how t ...

What is causing the javascript in my svg files not to function when embedded in an html document?

I have the following code for an SVG: <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg width="470px" height="260px" version="1.1" onload="addEvent ...

When the progress bar is clicked, the background color changes and then changes back again

https://www.w3schools.com/code/tryit.asp?filename=FG1ZE0NJ4ZX7 https://i.stack.imgur.com/Bnd0k.png I have created a progress bar that resembles the screenshot provided. When I hover over it, the color changes to green. However, I am looking for assistanc ...

Renaming form elements using JQuery's .load method

This is a page named A.html <form name=form> <input type=text id = txtA> </form> When I use jQuery to load it into B.html, it loads multiple times. <form name=form> <input type=text id = txtA> </form> <form name=f ...

Rails successfully processed the request with a 200 OK status code, however, the $.ajax() function is triggering the 'error' callback instead of the 'success' callback

In my controller, the ajax request is handled as shown below (simplified): def create ... @answer = Answer.new(video: video_file) if @answer.save render json: @answer.video.url else ... end end This is how I have defined my ajax function: $.aja ...

ESLint has detected a potential race condition where the `user.registered` variable could be reassigned using an outdated value. This issue is flagged by the `require-atomic-updates` rule

I have developed an asynchronous function which looks like this: let saveUser = async function(user){ await Database.saveUser(user); if (!user.active) { user.active = true; //storedUs ...

Angular Material's md-checkbox is a required component

I am working on a form that consists of checkboxes representing the days of the week. When the user hits submit without selecting any checkboxes, I want an error message to appear. Here is the HTML code snippet that I have: <form id="addEditForm" nam ...

Is it possible to utilize the returned value of a function within an if statement?

Is there a way to return the result of a function without needing to declare a variable? Can you return the result of a function in a single line? How can you return the result of a function inside an if statement? Is it possible to use a function's ...

Retrieve the date for the chosen time slot by utilizing the fullCalendar feature

I've been experiencing issues with a piece of code that is supposed to retrieve the date corresponding to a user-selected slot. Here's what I've tried so far: $('.fc-agenda-axis.fc-widget-header').on('mousedown', functio ...