1

I am trying to push a new property value into the existing JSONArray but when I do that I am getting the error TypeError: Cannot set property 'live' of undefined.

var ProxyData       =   [];

//Setting some of the fields outside the for loop
ProxyData.push({
                    'country'       :   country,
                    'ip'            :   ip,
                    'port'          :   port,
                    'createdAt'     :   createdAt,
                    'updatedAt'     :   updatedAt,
                    'provider'      :   'proxy11.com'
                });

//Based on some value I need to add thie live field
for(var item=0; item<ProxyData.length; item++)
{
    ProxyData[item].live    =   'Yes';

}

I want to retain the previously set objects but along with this based on some condition within the for loop I want to add the new field live to an object in the array. How can I achieve that?

I tried this also but no luck: ProxyData[item]["live"] = "Yes";

I tried defining the live field before so that I can set it later but still its not working:

ProxyData.push({
                            'country'       :   country,
                            'ip'            :   ip,
                            'port'          :   port,
                            'createdAt'     :   createdAt,
                            'updatedAt'     :   updatedAt,
                            'provider'      :   'proxy11.com',
                            'live'          :   ''
                        });

I posted the above code for simple understanding but to make it clear I will post the complete code from my project:

var ProxyData       =   [];

for(var itemCount=0; itemCount<pageData.data.length; itemCount++)
{
    var country     =   pageData.data[itemCount].country;
    var ip          =   pageData.data[itemCount].ip;
    var port        =   pageData.data[itemCount].port;
    var createdAt   =   pageData.data[itemCount].createdAt;
    var updatedAt   =   pageData.data[itemCount].updatedAt;

    ProxyData.push({
        'country'       :   country,
        'ip'            :   ip,
        'port'          :   port,
        'createdAt'     :   createdAt,
        'updatedAt'     :   updatedAt,
        'provider'      :   'proxy11.com'
    });

    itemProcessed++;

    if(itemProcessed == pageData.data.length)
    {

        var     sql     =   " INSERT INTO TBL_PROXY (IP, COUNTRY, PORT, CREATED_DT_TM, UPDATE_DT_TM, PROVIDER) VALUES ";

        for(var item=0; item<ProxyData.length; item++)
        {
            var proxy       =   'http://'+ProxyData[item].ip+':'+ProxyData[item].port;
            var liveState   =   "";

            request({
                'url':'localhost:3000',
                'method': "GET",
                'proxy': proxy
            },function (error, response, body) {

                if (!error && response.statusCode == 200) {
                    ProxyData[item].live    =   'Yes';
                    console.log(ProxyData);
                }
                else
                {
                    ProxyData[item].live    =   'No';
                    console.log(ProxyData);
                }
            });

            sql         +=  util.format(" (%s', '%s', '%s', '%s', '%s', '%s') ", ProxyData[item].ip, ProxyData[item].country, ProxyData[item].port, ProxyData[item].createdAt, ProxyData[item].updatedAt, ProxyData[item].provider)+',';
        }



        sql     =   sql.slice(0, sql.length-1);

        db.Proxy_Feed_Data_Push(sql, function(data){
            callback(data);
        });
    }
}
mangesh
  • 511
  • 8
  • 24
  • you can store your json as object `let ProxyData = {...} `and then add whatever property you want `ProxyData.live = "yes"` – Hachimi Ahmed Amine Jun 23 '20 at 12:11
  • Before the for loop I have another for loop which is initially populating the `ProxyData`. I have modified the code here so that it would be easy to answer. I have tried this before and it has worked but for some reason I am able to make it work now. – Sanjukta Ghosh Jun 23 '20 at 12:19
  • The code works as it is. Log `item` to the console before trying to use it. – Teemu Jun 23 '20 at 12:24
  • I am getting the following error when I try to continue further. If I remove the `ProxyData[item]["live"] = "Yes";` everything works fine. Not sure what is causing the issue. – Sanjukta Ghosh Jun 23 '20 at 12:27

1 Answers1

1

By the time your asynchronous call has finished, the value of "item" has changed. Try adding console.log(item, ProxyData[item]); after function (error, response, body) { to see.

Another way of seeing this phenomenon is with this simple example:

var ProxyData = ['a', 'b', 'c'];
for (var item = 0; item < ProxyData.length; item++) {
  Promise.resolve().then(() => {
    console.log(item, ProxyData[item]);
  });
}
// Results in:
// 3 undefined
// 3 undefined
// 3 undefined

In modern JavaScript environments, simply using let rather than var fixes this issue.

Since var is scoped to the function (not block) it is defined in, one workaround would be to (1) wrap every loop iteration with a function and (2) define a new variable (3) with the value of item so that when the synchronous call finishes, it uses the correct especially-defined-for-it variable.

    for(var item=0; item<ProxyData.length; item++)
    {
        // (1) wrap in a IIFE
        // (2) define a new variable
        (function(item2) {

            var proxy       =   'http://'+ProxyData[item].ip+':'+ProxyData[item].port;
            var liveState   =   "";

            request({
                'url':'localhost:3000',
                'method': "GET",
                'proxy': proxy
            },function (error, response, body) {
                // Even though 'item' has changed by now,
                // the variable 'item2' is scoped to the function
                // inside the loop iteration so it's what you expect.

                if (!error && response.statusCode == 200) {
                    ProxyData[item2].live    =   'Yes';
                    console.log(ProxyData);
                }
                else
                {
                    ProxyData[item2].live    =   'No';
                    console.log(ProxyData);
                }
            });

            sql         +=  util.format(" (%s', '%s', '%s', '%s', '%s', '%s') ", ProxyData[item].ip, ProxyData[item].country, ProxyData[item].port, ProxyData[item].createdAt, ProxyData[item].updatedAt, ProxyData[item].provider)+',';

        // (3) pass the value of 'item', it'll be assigned to 'item2'
        })(item);
    }

You could name the item2 variable item, but that could be confusing.

Or just use block-scoped variables in place of defining a function:

    let item2 = item;
    // When you run your async call later, it'll have
    // the correct value because 'let' is scoped to the 
    // nearest block (which is 'for() {}').
RickN
  • 12,537
  • 4
  • 24
  • 28