Steer clear of making changes to a JavaScript array

Here is my code snippet:

let traces = { ref: null, min: null, max: null, avg: null };
let learning = {
    "Application": "b3",
    "t": [
        {
            "d": 2,
            "BinaryType": "Current"
        },
        {
            "d": 3,
            "BinaryType": "Max"
        },
        {
            "d": 4,
            "BinaryType": "Avg"
        },
        {
            "d": 5,
            "BinaryType": "Min"
        }
    ]
};

let traceArr = Object.assign([],learning.t);

traceArr.forEach(trace => {

    if (trace.BinaryType == 'Current') {            
        traces.ref = Object.assign({}, learning);   
        traces.ref.t = [];
        traces.ref.t.push(trace);
        traces.ref.t[0].BinaryType = 'Refeyrence';            
    }

    if (trace.BinaryType == 'Min') {           
        traces.min = Object.assign({}, learning);
        traces.min.t = [];
        traces.min.t.push(trace);            
    }

    if (trace.BinaryType == 'Max') {            
        traces.max = Object.assign({}, learning);
        traces.max.t = []
        traces.max.t.push(trace);            
    }

    if (trace.BinaryType == 'Avg') {            
        traces.avg = Object.assign({}, learning);
        traces.avg.t = [];
        traces.avg.t.push(trace);            
    }          
});

console.log("Output",traces);
console.log("Traces- Should be non mutated",traceArr);
console.log("Original",learning.t)

It is my belief that modifying the array contents should not affect the content of the original object (learning).

I have two questions:

  • I assumed that traces.ref.t = []; would change the reference in the newly created object. However, this does not seem to be the case. Why is that?
  • The console.log("Original",learning.t) output shows that the content has been modified (the text Refeyrence was changed during array iteration). Why is this happening and how can I prevent it?

    'Original' [ { d: 2 , BinaryType: "Refeyrence" }, { d: 3 , BinaryType: "Max" }, { d: 4 , BinaryType: "Avg" }, { d: 5 , BinaryType: "Min" } ]

Answer №1

It seems like there are two related issues revolving around modifying a shared object.

My assumption was that setting traces.ref.t = []; would alter the reference in the newly created object. Is this not the case?

Using `[]` creates a new array instance, but `traces.ref` still references the shared `traces` object initially. To resolve this, you should create a copy of the `traces` object. Since it doesn't contain any nested values, using spread syntax is a simple way to achieve this in your situation:

const newTrace = { ...traces }

The output of console.log("Original", learning.t) shows that the content has been modified (the text "Reference" was changed during array iteration). Why is this happening and how can it be prevented?

This issue arises because the `trace` object is being pushed into the array and then modified. To work around this, you can also use object spread to create a shallow copy:

newTrace.ref.t.push({ ...trace });

With only these modifications, your original code may appear as follows:

let traces = { ref: null, min: null, max: null, avg: null };
let learning = {
  Application: "b3",
  t: [
    {
      d: 2,
      BinaryType: "Current"
    },
    {
      d: 3,
      BinaryType: "Max"
    },
    {
      d: 4,
      BinaryType: "Avg"
    },
    {
      d: 5,
      BinaryType: "Min"
    }
  ]
};

let traceArr = Object.assign([], learning.t);

traceArr.forEach(trace => {
  if (trace.BinaryType == "Current") {
    const newTrace = { ...traces };
    newTrace.ref = Object.assign({}, learning);
    newTrace.ref.t = [];
    newTrace.ref.t.push({ ...trace });
    newTrace.ref.t[0].BinaryType = "Reference";
  }

  // Other conditions and logic here...

});

console.log("Output", traces);
console.log("Traces - Should not be mutated", traceArr);
console.log("Original", learning.t);

Click here to view this code in the TypeScript playground. It may have a few type errors, but these changes should help prevent mutations.

Answer №2

It seems like Object.assign() doesn't perform a deep copy. Rather, it creates a new array where the elements still reference the original array. To achieve a deep copy, the easiest method is to convert it to JSON and then back again.

let traceArr = JSON.parse(JSON.stringify(learning.t));

However, a drawback of this approach is that if there are any circular references within the object being copied, this method will not work.

UPDATE:

Just to demonstrate what is happening, let's begin with the learning object:

let learning = {
    "Application": "b3",
    "t": [Object1, Object2, ...]
}

Then, the following occurs:

let traceArr = Object.assign([], learning.t);

This action creates a new array and duplicates the contents from learning.t into it. At this point, traceArr will appear like:

traceArr = [Object1, Object2, ...]

Subsequently, it iterates over the items in traceArr, which are essentially the same objects from learning.t, but now referenced from a different array.

if (trace.BinaryType == 'Current') {            
    // Here, trace === Object1

    // Creating a shallow copy of learning
    traces.ref = Object.assign({}, learning);
    // traces.ref = { "Application": "b3", "t": [Object1, Object2, ...] }

    // Generating a new array for traces.ref.t
    traces.ref.t = [];
    // traces.ref = { "Application": "b3", "t": [] }

    // Adding Object1 (which is the same object from learning.t) to the new array
    traces.ref.t.push(trace);
    // traces.ref = { "Application": "b3", "t": [Object1] }

    // Modifying Object1
    traces.ref.t[0].BinaryType = 'Reference';            
}

If you wish for the elements of traces.ret.t to not reference the same objects as those in learning.t, then you'll need to create new objects. In other words, pushing a copy of trace instead of just trace. Alternatively, as previously mentioned, make traceArr a deep copy of learning.t so that all the objects are fresh, and nothing is shared.

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

Submission error in Ripple-lib: 'UnhandledPromiseRejectionWarning - DisconnectedError: not opened'

I'm encountering an issue while trying to send Ripple XRP using ripple-lib and rippled server. Whenever I submit the signed payment object, I receive an error message stating: UnhandledPromiseRejectionWarning: DisconnectedError: not opened. Below is ...

Access various results from a jQuery function

Is there a way to efficiently extract the values of petKeys and employeeKey using the jQuery functions provided below? var whenSelectDateFromCalendar = function () { initKeyValues(); petKeys = ? employeeKey = ? }; var initKeyValues = function ...

When I attempt to utilize the API, the JavaScript implementation of a <script src=...> element seems to interfere

Within one of my HTML files, I encountered the following line near the top: <script src="//maps.google.com/maps/api/js?key=apikey"></script> The API key is currently hardcoded in this file, but I would like to use a configuration option store ...

A compilation of approved classes for the quill toolbar

The instructions provided in the documentation for the quill module indicate that you can manually create a toolbar using HTML, and then pass the corresponding DOM element or selector to Quill. This will result in the ql-toolbar class being added to the to ...

What is a method for capturing the value of a nested input element without requiring its ID to be

Currently, I am facing a challenge in selecting the value of an input box from a user-generated ordered list of text boxes and logging its value to the console. It seems like I cannot find a way to select the value of a child's child element when the ...

Is there a way to generate and transmit a text file using XmlHttpRequest or $.ajax?

The server is anticipating an html or txt file to be sent from a form named "websitetopdf". The file is dynamically created on client javascript and should only function properly on Chrome. Below is the form that needs to be used for sending: <form ac ...

Using Pug/Jade, be sure to call a function during each loop iteration

I'm attempting to develop a basic app, similar to a TO-DO LIST. I want to generate divs dynamically (with incremental ids) when a Button is clicked and then enter some text into an HTML input field. For example: <div id="item1"> <div id="ite ...

What are the ways to implement global functions in Vue.js?

I have a function that formats dates without time. I want to reuse this function in multiple components. What is the recommended approach for handling this scenario? Should I use directives, filters, or another method? How should I go about defining this ...

fade out row upon successful deletion using ajax

My code includes an AJAX function to delete items by row. In my table, there is a column with the action to perform the deletion. Here is the AJAX function: //delete detail function deleteDetail(id_po_req_detail) { var tr = ...

What is the process of comparing one regular expression against another in JavaScript?

I'm looking to check for matches between two regular expressions. To achieve this, I use the .test method on each one. If either of them returns true, I consider them a match. const regexify = (str) => new RegExp( '^' + str ...

JavaScript taking over the HTML footer content

I'm attempting to update the content of my footer without modifying the HTML by including a class or an id. HTML: <footer> <div> Lorem ipsum dolor sit amet, consectetur adipisicing elit. </div> </footer> Java ...

Packing third-party npm modules with Webpack for seamless integration

Description I am currently working on a project that involves nodejs, TypeScript, and express. The source files with a *.ts extension are being bundled using webpack, while the node_modules folder is excluded using webpack-node-externals. However, when I ...

Introducing a detailed model showcasing information sourced from an array of objects within Angular 14

I'm struggling to showcase detailed models for various elements in my project. In essence, I have a collection of cards that hold diverse information data. The objective is simple: upon clicking a button to reveal more details about a selected card, a ...

Class methods cannot be invoked within their constructor

I'm currently facing challenges with implementing my express router. This is a "subrouter" of my main router, hence I need to extend express.Router. Here's an example of the code (simplified to include only one method): import express from "expr ...

Is there a way to remove specific items from my array in Vue.js?

Essentially, I am using an HTML select element that is populated with arrays of registered users. <label > <b> <i style="opacity:0.8">Users:</i> </b> </label>&nbsp;&nbsp; <select class=&quo ...

Using an arrow function in Aurelia to read a json file

I've been exploring Aurelia and delved into tutorials on PluralSight and Egghead.io, but I'm still struggling to resolve my current issue. In a JSON file named bob.json, there is a collection of objects related to Bob. Each object in the collect ...

Encountering a problem sending an array of objects to a controller via jQuery AJAX

I attempted to send an array of objects to a controller using jQuery Ajax, but I am getting a null result in ASP.NET 5.0. The data array that I'm sending is named regions. The constructor for the data is defined in the BoundingBoxModel class. Here i ...

Guide on Redirecting in Express.js

I am seeking assistance with finding solutions. In short, I have created this API () with username: test and token: test. If the current date is past the meeting time, then open the meeting URL in a new tab. This check should occur every second. ...

The functionality of transparent code on Three.js appears to be not functioning properly

I have researched and attempted various solutions, but unfortunately none of them have been successful. Here is an example of what I have tried: var renderer = new THREE.WebGLRenderer( { alpha: true } ); and root.renderer.setClearColor(0xffffff,0); De ...

Did the IBM MobileFirst client miss the call to handleFailure?

I am currently utilizing the IBM MFP Web SDK along with the provided code snippet to send challenges and manage responses from the IBM MobileFirst server. Everything functions properly when the server is up and running. However, I have encountered an iss ...