Home > Software engineering >  Only last object is being pushed to object array (JavaScript)
Only last object is being pushed to object array (JavaScript)

Time:06-10

I have a function which essentially creates an object and then pushes each newly created object to an array of objects. However I am facing an issue where only the last object being created (in my for loop) is being pushed to my object array.

I firstly declare my object and object array:

this.locationObjectArray = [];
this.locationObject = {};

I then declare a variable "that" equal to "this" so that I can use the above declared object and object array in my callbacks:

var that = this;

I then read an odata entity which returns a success callback with the specified entities data:

oModel.read("/Customers", {
                success: function (resp) {
                    var promises = [];
                    for (var x = 0; x < resp.results.length; x  ) {
                        // Set tooltip property
                        var tooltip = resp.results[x].Country;
                        that.locationObject.tooltip = tooltip;
                        // Set type property
                        that.locationObject.type = "Inactive";
                        // Set position property
                        var request = $.ajax({
                            url: "https://api.opencagedata.com/geocode/v1/json?key=d3e40bf9b50247d4acf8122c543c5187&q="   tooltip,
                            type: "GET",
                            dataType: 'json',
                            success: function (resp) {
                                that.locationObject.pos = resp.results[0].geometry.lng   ";"   resp.results[0].geometry.lat   ";0";
                            }
                        });
                        // Populate promises and locationObjectArray     
                        that.locationObjectArray.push(that.locationObject);
                        promises.push(request);
                        console.log(that.locationObject);
                    }
                    $.when.apply(null, promises).done(function(){
                        // console.log(that.locationObjectArray);
                        that.getView().getModel("map").setProperty("/Spots", that.locationObjectArray);
                    })
                }
            })

I created an array called promises as in my for loop I am making an Ajax request to an API which will return a promise each time it loops. I need to contain each request (promise) so that I can set the remainder of the function to run only once all of the data requested in each request (of each loop) is received.

I then create my for loop.

Within the for loop I begin to set each property of the previously declared "locationObject". The properties are: "tooltip" (locationObject.tooltip), "type" (locationObject.type) and "pos" (locationObject.pos). Both properties "tooltip" and "type" are straight forward. It is only the property "pos" that is causing difficulties as this data is returned as the result of a promise success callback in each loop.

Nevertheless, I then push the newly created object to the object array and also push that loops promise to the promises array.

Once the for loop has finished and all promises are resolved, I then set my data model.

The above would appear as fine, although the resulting object array (locationObjectArray) comes out like this:

enter image description here

The entire array is populated (for each loop) with the data collected in the locationObject for the final loop.

I have attempted to reset the object (locationObject) at the beginning of each loop:

...
for (var x = 0; x < resp.results.length; x  ) {
    that.locationObject = {};
    ...

However that interferes with the Ajax request to get the "pos" property data for each object, and the resulting array is constructed successfully with only the "tooltip" and "type" properties set:

enter image description here

I am not sure what I could do to get around this, or why the promises are now ignored, any suggestions / advice will be appreciated.

CodePudding user response:

There are several issues in this code. The main one I see is that you only have one locationObject that you keep modifying and pushing into the array over and over again. Since .push() pushes a reference to that object (does not make a copy), you just end up with an array of all the same object in it and it has the value from the last item that you processed.

You can fix that main issue by just creating a new locationObject in your loop. Here's what I would suggest for a rewrite:

oModel.read("/Customers", {
    success: async function(resp) {
        for (let result of resp.results) {
            const locationObject = {};
            const tooltip = result.Country;
            locationObject.type = "Inactive";
            const geoData = await $.ajax({
                url: "https://api.opencagedata.com/geocode/v1/json?key=d3e40bf9b50247d4acf8122c543c5187&q="   tooltip,
                type: "GET",
                dataType: 'json',
            });
            locationObject.pos = geoData.results[0].geometry.lng   ";"   geoData.results[0].geometry.lat   ";0";
            that.locationObjectArray.push(that.locationObject);
        }
        that.getView().getModel("map").setProperty("/Spots", that.locationObjectArray);
    }
});

This uses the promise that $.ajax() returns and gets rid of the success callback for $.ajax(). It also simplifies the code by serializing the for loop with await.


If you want to run all the ajax calls in parallel and avoid using await, you can do it this way:

oModel.read("/Customers", {
    success: function(resp) {
        Promise.all(resp.results.map(result => {
            const locationObject = {};
            const tooltip = result.Country;
            locationObject.type = "Inactive";
            return $.ajax({
                url: "https://api.opencagedata.com/geocode/v1/json?key=d3e40bf9b50247d4acf8122c543c5187&q="   tooltip,
                type: "GET",
                dataType: 'json',
            }).then(geoData => {
                locationObject.pos = geoData.results[0].geometry.lng   ";"   geoData.results[0].geometry.lat   ";0";
                return locationObject;
            });
        })).then((locationArray) => {
            that.getView().getModel("map").setProperty("/Spots", locationObjectArray);
        });
    }
});

Note, this avoids using that.locationObject and that.locationObjectArray entirely. The results are collected and used internally without those higher scoped variables.

  • Related