JavaScript loop an array - ReferenceError | i is not defined - javascript

newbie at JavaScript and Postman here.
I have set up a basic test in postman using JS to compare names in a web response to names in a data file. The array of names is in an external data csv file.
I want to loop through the array, but I get an error:
"ReferenceError | i is not defined"
Code:
var newResponse = responseBody;
let nameArray = data.name;
for (let i = 0; i < nameArray.length; i++) {
console.log(nameArray.length);
}
pm.test("Web vs. Data: Person", function() {
pm.expect(newResponse.Item[i].name).to.equal(nameArray.Item[i].person);
});
console.log(newResponse.Item[i].name);
console.log(nameArray.Item[i].person);

Your end scope "}" character missing please change with this code;
var newResponse = responseBody;
let nameArray = data.name;
for (let i = 0; i < nameArray.length; i++) {
console.log(nameArray.length);
pm.test("Web vs. Data: Person", function () {
pm.expect(newResponse.Item[i].name).to.equal(nameArray.Item[i].person);
});
console.log(newResponse.Item[i].name);
console.log(nameArray.Item[i].person);
}

let is block scoped so it will cause ReferenceError out of the for loop. The variable i will not be referred outside of the for loop. So you've to move your codeblock inside the for loop like below. Hope this helps :)
var newResponse = responseBody;
let nameArray = data.name;
for(let i = 0; i < nameArray.length; i++){
console.log(nameArray.length);
pm.test("Web vs. Data: Person" ,function(){
pm.expect(newResponse.Item[i].name).to.equal(nameArray.Item[i].person);
});
console.log (newResponse.Item[i].name);
console.log(nameArray.Item[i].person);
}

Related

Accessing varible ouside of arrow functions scope

For some reason I can't set a variable from inside a arrow function that is set outside of it. This is using replit's database. When I send the message containing the embed in the arrow function it works, but it will be in separate embeds.
Here is my code:
ARDB.list().then(keys => {
const ARListEmbed = new Discord.MessageEmbed()
.setColor('#0099ff')
for (var I=0; I<keys.length; I++) {
ARDB.get(keys[I]).then(value => {
ARListEmbed.addField("["+(I+1)+"]"+" "+keys[I], value, true);
}));
};
message.channel.send(ARListEmbed);
});
Any and all help will be appreciated!
Thanks for your help! But I seemed to fix this myself.
Here is the code that worked somehow.
ARDB.list().then(keys => {
const Length = keys.length;
let Index = 0;
let ARListEmbed = new Discord.MessageEmbed()
.setColor('#0099ff')
.setTitle('9NK Discord Server Auto Responses:')
.setURL('https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=OfficialRickAstley')
for (var I=0; I<keys.length; I++) {
let Key = keys[I];
ARDB.get(keys[I]).then(value => {
Index++;
ARListEmbed.addField("["+(Index)+"]"+" "+Key, value, true);
if (Index == Length) {
message.channel.send(ARListEmbed);
};
});
};
});
This code works because Index can be changed within the arrow function and keeps it's changed value when the for loop loops again but ouside of the loop it says it's original self (0). So I send the message when Index == keys length.

Why an element in an object is always assigned the same value in a loop | vueJS

I think I have a problem understanding how javascript or more precisely vuejs in this case works...
the following code is given:
....
data() {
campaigns: [{id: 1, name: 'Campaign 1'}, {id: 2, name: 'Campaign 1'}]
campaignsWithEventSettings: [0, 1] //these are the indexes for campaigns
},
methods: {
saveOrUpdate() {
let campaigns = [];
this.campaignsWithEventSettings.map(x => {
let campaign = this.campaignsSettings[x];
campaign.event_settings = true;
console.log(this.campaigns[x].id) //screenshot with the result is attached below
campaign.id = this.campaigns[x].id;
campaigns.push(campaign);
});
//that gives the same result
/*for (let i = 0; i < this.campaignsWithEventSettings.length; i++) {
let idx = this.campaignsWithEventSettings[i];
let campaign = this.campaignsSettings[idx];
campaign.event_settings = true;
campaign.id = this.campaigns[idx].id;
campaigns.push(campaign);
}*/
let settings = [];
settings.push({eventId: this.eventId});
settings.push({campaigns: campaigns});
axios.post('/events/campaigns', settings).then(resp => {
console.log(resp)
})
},
}
....
the problem is that in the end, all campaigns have the same id, although when running console.log the log ids are different/correct.
so in other words at each loop, all campaigns in the array receive a new id (the last one).
console.log
screenshot_console
request
screenshot_request_data
so I will quote what the problem is and I put the link with more detailed information.
When you use a variable that is declared outside the scope the variable is going to be used in, it will use the value that variable has at the time it runs. It doesn't get a copy of the value at the time the closure is setup. If you think of closures as pointers rather than values, maybe that will help.
sorce: https://dzone.com/articles/why-does-javascript-loop-only-use-last-value
this means that the variable that your let campaign = this.campaignsSettings[x]; is using is always the last one of the X. Because your X is the variable out of the scope, this is very often when you try to output the index of an loop.
A quick solution is to wrap in a function!
for(let x of this.campaignsWithEventSettings){
(function (newX){
let campaign = this.campaignsSettings[newX];
campaign.event_settings = true;
campaign.id = this.campaigns[newX].id;
campaigns.push(campaign);
})(x);
}
or something like
for(let x of this.campaignsWithEventSettings){
try{throw x}
catch(newX){
let campaign = this.campaignsSettings[newX];
campaign.event_settings = true;
console.log(this.campaigns[newX].id)
campaign.id = this.campaigns[newX].id;
campaigns.push(campaign);
}
}
I'd do the following:
let campaigns = this.campaignsWithEventSettings.map(x => {
let campaign = this.campaignsSettings[x];
campaign.event_settings = true;
campaign.id = this.campaigns[x].id;
return campaign
});

how do I check if a value is in a JSON return or not?

I'm writing a test in postman where I want to check if a JSON return contains the Label called 'RegressieMapTest'. This is my script:
pm.test("Is the folder created correctly?", function(){
var jsonData = pm.response.json();
var objString = JSON.stringify(jsonData);
var obj = JSON.parse(objString);
for (var i = 0; i < obj.Corsa.Data.length; i++){
if (obj.Corsa.Data[i].Label == "RegressieMapTest"){
console.log(obj.Corsa.Data[i].Label);
pm.expect(obj.Corsa.Data.Label).to.eql("RegressieMapTest");
}
}
pm.expect.fail();
})
But it doesn't quite work, every time I run this script it seems like it automatically jumps to pm.expect.fail() which is weird because 'RegressieMapTest' is inside the JSON return. Postman returns the following message:
Is the folder created correctly? | AssertionError: expect.fail()
pm.respose.json() is equalent to JSON.parse you don't have to do it again
also you can use array.find method instead of looping through it
pm.test("Is the folder created correctly?", function () {
var jsonData = pm.response.json();
pm.expect(obj.Corsa.Data.find(elem => elem.Label === "RegressieMapTest")).to.be.not.undefined
}
if array has any element with label RegressieMapTest then it will return that data elese returns undefined, so we are validating that it will not return undefined. Meaning it has the value
Your pm.expect.fail(); always runs. You want it to run only when you don't find the field. So just add a flag in your check block.
pm.test("Is the folder created correctly?", function(){
var jsonData = pm.response.json();
var objString = JSON.stringify(jsonData);
var obj = JSON.parse(objString);
var isFound = false;
for (var i = 0; i < obj.Corsa.Data.length; i++){
if (obj.Corsa.Data[i].Label == "RegressieMapTest"){
console.log(obj.Corsa.Data[i].Label);
pm.expect(obj.Corsa.Data.Label).to.eql("RegressieMapTest");
isFound = true;
}
}
if (!isFound) {
pm.expect.fail();
}
})

My callback functions don't seem to fill in my array?

I have a problem with my callback functions. My code is supposed to make 16 GET requests to a REST API to pull 16 different JSON files. It then needs to parse each of these JSON's to a dictionary for that week's football table rankings, and ultimately save each entry into a 'dictionary of dictionaries', HistoricalTable, to give the league ranking for the past 16 weeks. However, when I run the associated callback functions, the various LeagueTable variables seem to work fine, but when I try and save these into the Historical Data, the final array appears to have the same LeagueTable entry for each, looking like this.
Here is an image of the console output for my final table. Each entry should be different, whereas each entry seems to be the most recent week.
//This creates the modifier for the URL used in the GET request
var MatchDayList = []
for (i = 0; i < 17; i++) {
MatchDayList[i] = i
}
MatchDayList.shift()
var HistoricalTable = {}
var LeagueTable = {}
// This executes the GET request
for (i = 0; i < 16; i++) {
url = 'http://api.football-data.org/v1/competitions/445/leagueTable/?matchday=' + MatchDayList[i],
$.ajax({
url: 'http://api.football-data.org/v1/competitions/445/leagueTable/?matchday=' + MatchDayList[i],
headers: {
'X-Auth-Token': ''
},
method: 'GET',
dataType: 'json',
success: function(data) {
handleData(data)
},
});
}
//This function should append the retrieved JSON to the LeagueTable variable
function handleData(data) {
for (var j = 0; j < 20; j++) {
LeagueTable[data.standing[j].position] = data.standing[j].teamName
LeagueTable[20] = data.matchday
}
saveData(LeagueTable)
}
//This function should save each LeagueTable matchday data into a bigger array, HistoricalTable
function saveData(LeagueTable) {
HistoricalTable[LeagueTable[20]] = LeagueTable
console.log(HistoricalTable)
}
You are using a single LeagueTable variable throughout the entire code. So every call to handleData populates the same LeagueTable, then tells saveData to store it in the main table. So you end up with 16 references to the same table.
To solve it, it should be enough to move the variable declaration inside handleData function:
function handleData(data) {
var LeagueTable = {};
for (var j = 0; j < 20; j++) {
LeagueTable[data.standing[j].position] = data.standing[j].teamName
LeagueTable[20] = data.matchday
}
saveData(LeagueTable)
}
On a side note, your url variable is not declared anywhere, so it ends up in the global scope, which is generally bad practice. Same with i indices inside your for loops.

How to use external variable inside promise response of $http.get [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
Here is my code
for (var i=0; i<5; i++) {
var url = generate_url(i) ;
$http.get(url).then(function(response){
var param2 = response.data.param2
$scope.outputData.push({'index':i, 'param':param2}) ;
}) ;
}
In this example, I suppose to obtain an array in the $scope.outputData with data similar to this:
[
{'index':0,param:'a'},
{'index':1,param:'b'},
{'index':2,param:'c'},
{'index':3,param:'d'},
{'index':4,param:'e'},
]
but what i get is data like this:
[
{'index':4,param:'a'},
{'index':4,param:'b'},
{'index':4,param:'c'},
{'index':4,param:'d'},
{'index':4,param:'e'},
]
In this case, the externel data that I mean is the variable i,
Please could you tell me the trouble ? and how do I proceed to attend my goal? Thank you in advance and sorry for my english :)
You can create a closure over the i variable to make sure it still has the value you want it to have when you use it.
for (var i=0; i<5; i++) {
(function(counter) {
var url = generate_url(i);
$http.get(url).then(function(response){
var param2 = response.data.param2
$scope.outputData.push({'index':counter, 'param':param2}) ;
});
}(i));
}
But if the ordering of the resulting array matters, you will have to create a temporary array and then sort it on index.
You can use $q.all if you don't want to process any of the requests untill they have alle completed.
Something like this:
var promises = {};
for (var i=0; i<5; i++) {
var url = generate_url(i) ;
promises[i] = $http.get(url);
}
$q.all(promises).then(function(result) {
for (index in result) {
var param2 = result[index].data.param2
$scope.outputData.push({'index':index, 'param':param2}) ;
}
});
That should also preserve the ordering.
The docs for $q are here.
This is a closure issue, the correct way to do it would be
for (var i=0; i<5; i++) {
getData(i);
}
var getData=function(index) {
var url = generate_url(index) ;
$http.get(url).then(function(response){
var param2 = response.data.param2
$scope.outputData.push({'index':index, 'param':param2}) ;
}) ;
}

Categories

Resources