I was filling an array directly from a promise, but then I realized I need to add some html to the array so I was attempting to use a for loop.
pullData().then((response) => {
//previously populated medication array from response like this
this.setState({medications: response});
//now I wish to loop how can I put data in , push data in?
for (let i = 0; i < response.length; i++) {
// push data into array?
this.medications.push <a onclick={`jsfunction(${response[i].value}) href=javascript:void(0);`}>{`${response[i].value}`}</a>;
}
});
I realize there are map functions, but I just want to get data into my array. there are two columns, "value" and "short_description"
The problem that you are mentioning seems unclear. As per my understanging, you want to fetch those data and show it as a list.
Here try this one
pullData().then((response) => {
let adding_list=[];
//if you want to append the response in previous list uncomment below line
let adding_list=this.state.medications;
//beleiving that the response is an array
response.map((item)=>{
adding_list.push( <a onclick={`jsfunction(${item.value}) href=javascript:void(0);`}>{`${item.value}`}</a>);
}
this.setState({medications:adding_list});
});
You cant just update state by calling this.medications.push()
You can update state inside for loop like this:
for (let i = 0; i < response.length; i++) {
let newMedication = <a onclick={`jsfunction(${response[i].value}) href=javascript:void(0);`}>{`${response[i].value}`}</a>;
this.setState(prevState => {
return {
medications: [...prevState.medications, newMedication]
}
})
}
Or you can save the new medications to a new array and then update state:
let allMedications = [];
for (let i = 0; i < response.length; i++) {
allMedications.push(<a onclick={`jsfunction(${response[i].value}) href=javascript:void(0);`}>{`${response[i].value}`}</a>);
}
this.setState({
medications: allMedications
})
I ended up doing this
pullData().then((response) => {
const modifiedResponse = response.map((item) => {
const modifiedValue = <a onClick={() => this.jsfunction(item.value, item.short_description)} href="javascript:void(0);">{item.value}</a>;
item.value = modifiedValue;
return item;
});
this.setState({medications:modifiedResponse});
});
Related
Currently trying to figure out why dataset[i]["stationId"] is coming back as undefined.
I have tried to use the data I set as well but it would return undefined as well. However, If I console log something like dataset[0]["stationId"] , I'd be able to get something back but just not in a for loop. What I'm trying to do is to restructure the data I receive from the call.
Thanks in advance for answering !
const [data, setData] = useState([]);
let arr= []
var i;
useEffect(() => {
const fetchEvents = () => {
fetch(`http://${ip.trim()}:${port}/events/station`)
.then(response => response.json())
.then(json => {
setData(json);
console.log(data)
})
.then(dataset =>{
if(arr.length === 0){
arr[0] = {stationId : dataset[0]["stationId"], event_type: dataset[0]["event_type"], duration: [dataset[0]["duration"]]}
}
for(i=0; i<arr.length; i++){
for(i=0; i<dataset.length; i++){
// if station id is not the same
if(arr[i]["stationId"] !== dataset[i]["stationId"]){
arr.push(dataset[i])
}
else if(arr[i]["event_type"] === dataset[i]["event_type"]){
arr[i]["duration"].push(dataset[i]["duration"])
}
else{
arr[i]["event_type"] = dataset[i]["event_type"]
arr[i]["duration"] = [dataset[i]["duration"]]
}
}
}
})
.catch(
err => {
console.log(err);
}
);
};
fetchEvents();
}, []);
As noted in the comments, you two for loops use the same counter. After every pass through the inner for loop, the i gets increased, and then in the outer for loop it gets increased again. At some point the i will have increased to much and dataset[i] will be undefined.
Putting the inner for loop in a function would have countered this problem, like do:
for(i=0; i<arr.length; i++){
arr[i] = check(dataset, arr[i]);
}
function check(dataset, arri){
for(var i=0; i<dataset.length; i++){
// if station id is not the same
if(arri["stationId"] !== dataset[i]["stationId"]){
arri.push(dataset[i])
}
else if(arri["event_type"] === dataset[i]["event_type"]){
arri["duration"].push(dataset[i]["duration"])
}
else{
arri["event_type"] = dataset[i]["event_type"]
arri["duration"] = [dataset[i]["duration"]]
}
}
return arri
}
But, there is another problem, the push method. Say, the arr[i] is as follows:
arr[i] === {stationId : 1, event_type: "a_type", duration: [1,2,3]}
Then doing arr[i].push(dataset[i]) will not work. The push method is for Arrays. You are trying to push something on an Object. Objects do not have a push method...
thanks in advance for your time.
I am looping 2 services endpoints to get data, the problem that I am facing is that I would like to wait for the first subscribe to finish in order to loop the other one, but the result outside of the first subscribe is empty and if I place the second loop within the previous subscribe, I am facing duplications and/or undefined variables.
I have tried two ways
The first solution i have tried is looping the service endpoint and then looping the other service endpoint inside the first subscribe. The problem with this solution is that i am facing duplicates and/or undefinend variables.
The second solution i tried is using forkJoin, but i have two problems with this one.
The first problem i have using forkJoin is that i am getting a Pageable from my backend (using java - spring boot) to manage the pagination, so i cannot do a proper pagination because the forkJoin retrieve the Pagination in an Array, so the details are no longer useful, since it is splitted in the array.
The second problem i have using forkJoin is that everytime i click again to fetch the data it is adding the result to the old one, making endless duplicates (even when i set my arry to empty everytime i press the button)
I will share with you guys both examples i have tried and the service endpoints i am using
Service 1:
getAllQuestionsEvaluation(page: number, level: number, profu: number, idComponent: number): Observable<any> {
return this.http.get(this.urlEndpoint + 'page/' + page + '/' + level + '/' + profu + '/' + idComponent, {headers: this.headerService.addAuthHeader()}).pipe(
catchError((e) => {
this.headerService.serverError(e);
if (this.headerService.isNoAuth(e)) {
this.appDialogService.confirm();
}
return throwError(e);
})
);
}
I first loop that endpoint and then i need to loop the result for the next service to get the other data:
Service 2:
getAllByIdQuestion(idQuestion: number): Observable<any> {
return this.http.get(this.urlEndpoint + 'findbyidq/' + idQuestion, {headers: this.headerService.addAuthHeader()}).pipe(
catchError((e) => {
this.headerService.serverError(e);
if (this.headerService.isNoAuth(e)) {
this.appDialogService.confirm();
}
return throwError(e);
})
);
}
The first code i have been trying is the following, however i and facing duplicates and problems with the result:
jsonComponentsResult is the array result of components, however that service is not looping, so i am facing no problems with it and that is why i am not sharing that code.
this.questions = [];
this.questionsToCompare = [];
for (let com of jsonComponentsResult) {
this.questionService.getAllQuestionsEvaluation(0, this.evaluation.level, this.evaluation.profu, com.idComponent.idComponent).subscribe(
(jsonQuestions) => {
this.questionsToCompare = this.questionsToCompare.concat(jsonQuestions.content);
for (let i = 0; i < this.questionsToCompare.length; i++) {
this.questionsGradeService.getAllByIdQuestion(this.questionsToCompare[i].idQuestion).subscribe(
(response) => {
this.questionsGrades = this.questionsGrades.concat(response);
for (let p of this.questionsGrades) {
if (this.evaluation.idGrade.idGrade === p.idGrade.idGrade) {
this.questions = this.questions.concat(p.idQuestion);
}
}
this.dataSource = new MatTableDataSource < Questions > (this.questions);
}
);
}
this.pageIndex = jsonQuestions.number;
this.pageSize = jsonQuestions.size;
this.length = jsonQuestions.totalElements;
},
(error) => {
console.log(error);
}
);
}
My second solution is to use ForkJoin, however as i mentioned before, i am not able to manage my pagination and i am getting duplicates everytime i press the button even though i am setting the arrays to empty []:
observables: Observable < Questions > [] = [];
observablePG: Observable < QuestionsGrade > [] = [];
this.questions = [];
this.questionsToCompare = [];
this.questionsGrades = [];
for (let i = 0; i < jsonComponentResult.length; i++) {
this.observables.push(this.questionService.getAllQuestionsEvaluation(0, this.evaluation.level, this.evaluation.profu, jsonComponentResult[i].idComponent.idComponent));
}
Observable.forkJoin(this.observables).subscribe(
(response) => {
for (let x of response) {
this.questionsToCompare = this.questionsToCompare.concat(x);
}
this.observables = [];
this.getQuestions(this.questionsToCompare);
},
(e) => {
console.log(e);
}
);
private getQuestions(questionsArray: any) {
for (let i = 0; i < questionsArray.length; i++) {
this.observablePG.push(this.questionsGradeService.getAllByIdQuestion(questionsArray[i].idQuestion));
this.questions = [];
this.questionsToCompare = [];
}
Observable.forkJoin(this.observablePG).subscribe(
(response) => {
for (let x of response) {
this.questionsGrades = this.questionsGrades.concat(x);
}
this.observablePG = [];
for (let p of this.questionsGrades) {
if (this.evaluation.idGrade.idGrade === p.idGrade.idGrade) {
this.questions = this.questions.concat(p.idQuestion);
}
}
this.dataSource = new MatTableDataSource < Questions > (this.questions);
},
(e) => {
console.log(e);
}
);
}
I would like to wait for the first subscribe to finish in order to loop the other one, but i haven't been able to find a solution, since it looks like looping those services at the same time are causing troubles, and with the forkJoin i am losing my pagination and i am getting all the list again everytime i press the button.
I would like to get the questions list, then, get the questionsGrades to compare if i should add that question to my questions array.
Sorry for the long post and thank you for your time.
Maybe rxjs operator decision tree page could help you with this.
And the second, I remember that I had a similar issue, the only difference was that the value from the first Observable was used as a param in a second one, and for that I used switchMap operator.
I am building a simple application that stores places I've visited. I have a local express server using a db.json file as my database. I am making 2 requests and experiencing a problem.
What I'm trying to do is iterate over both arrays so that when the app loads, countries I've been too are already preselected. this seems like a super expensive call to make and has quite slow performance already
Also it's not actually doing what I want until I trigger a second re-render of the DOM and then it updates.
e.g. if I pre-select Croatia and France in the database and then load the app, none are selected. but if I then select Korea (e.g.) then in the visited list, suddenly all 3 are visible
what would be a better way to compare the arrays? considering the object keys are not necessarily the same
componentDidMount(){
axios.get('https://restcountries.eu/rest/v2/all').then((data) => {
const updatedCountries = data.data.map((country) => {
return {...country, visited: false, cities: [], showCities: false}
})
axios.get('http://localhost:3007/countries').then((countries) => {
const visitedCountries = countries.data
for (var i = 0; i < visitedCountries.length; i++){
for (var k = 0; k < updatedCountries.length; k++){
if(visitedCountries[i].name === updatedCountries[k].name){
updatedCountries[k].visited = true
}
}
}
})
this.setState({countries: updatedCountries})
})
}
Instead of using an array to store updatedCountries, you should instead use an object. That way instead of having each element of updatedCountries compare to every element of visitedCountries, you can do a constant lookup. This will change your lookup speed from (n*n) to (n).
The reason why you do not initially see any updates is because you have an async call:
axios.get('http://localhost:3007/countries')
inside of a synchronous function. As a result, you are resetting the state while you are making the get request. Instead you should chain your api calls like
axios.get('https://restcountries.eu/rest/v2/all').then((data) => {
// edit data
return axios.get('http://localhost:3007/countries')
}).then((data) => {
// run function comparing both data
this.setState({countries: updatedCountries})
})
You need update state in second request success callback function
componentDidMount(){
axios.get('https://restcountries.eu/rest/v2/all').then((data) => {
const updatedCountries = data.data.map((country) => {
return {...country, visited: false, cities: [], showCities: false}
})
axios.get('http://localhost:3007/countries').then((countries) => {
const visitedCountries = countries.data
for (var i = 0; i < visitedCountries.length; i++){
for (var k = 0; k < updatedCountries.length; k++){
if(visitedCountries[i].name === updatedCountries[k].name){
updatedCountries[k].visited = true
}
}
}
this.setState({countries: updatedCountries})
})
})
}
For efficient way to search
axios.get('http://localhost:3007/countries').then((countries) => {
let visitedCountriesName = new Set(countries.data.map(country => country.name));
updatedCountries = updatedCountries.map((country) => {
if (visitedCountriesName.has(country.name)) country.visited = true
return country
});
this.setState({countries: updatedCountries})
})
I declared an array, But when I push elements inside it, it remains Empty. Here's my Code :
var catsObjectId = new Array();
var data = new Array();
Recipe.find((err,doc3)=> {
data = doc3;
for (var i = 0; i < data.length; i++) {
catsObjectId.push([]);
data[i]['categories'].forEach((item, index) => {
Recipecat.findOne({_id: item}, (err,result)=> {
item = result.name;
catsObjectId.push(item);
});
})
}
console.log(catsObjectId);
});
Here's the Recipe schema :
var recipeSchema = Schema({
categories: [{
type: Schema.Types.ObjectId,
ref: 'RecipeCat',
}]
});
and Here's the Recipecat schema :
var recipecatSchema = new Schema({
name: {
type: String,
required: true
}
});
I want to replace objectIds for recipeCats with their names.
When I log 'catsObjectId', It shows an empty array.
What Seems to be the Problem?
Thanks In advance!
(I understand this question is a bit old, but if you still need help)
That's because you're pushing to an array which is outside the callback and the async nature of JavaScript kicking in.
Here's simple explanation why it's empty
var catsObjectId = new Array();
var data = new Array();
Recipe.find((err,doc3)=> {
// say execution 1
for (var i = 0; i < data.length; i++) {
catsObjectId.push([]);
data[i]['categories'].forEach((item, index) => {
// say execution 2
Recipecat.findOne({_id: item}, (err,result)=> {
item = result.name;
catsObjectId.push(item);
});
})
}
// say execution 3
console.log(catsObjectId);
});
First execution 1 is executed. Within this forEach iterates over each item and fires execution 2. Then continues to execute execution 3.
The problem is execution 2 is asynchronous and the value is returned sometime in the future. This future is after excution 3 is executed. When Recipecat.findOne finishes execution, the callback within then(result.. is called. But console.log(catsObjectId) is already executed and catsObjectId was empty at the time of execution.
You should either use catsObjectId within the callback .then((data) => // use data here) or use the async/await to make it sync like.
Note await is only valid inside async function
async function getSomeNames() {
try {
const data = await Recipe.find();
// docs is an array of promises
const docs = data.map((item, index) => {
Recipecat.findOne({_id: item})
});
// items is an array of documents returned by findOne
const items = await Promise.all(docs);
// now you can map and get the names
const names = items.map(item => item.name);
} catch (e) {
// handle error
console.error(e);
}
}
getSomeNames()
Your pushing an empty array every time it goes through the for loop. Try deleting this line.
catsObjectId.push([]);
You have to use promises in order to control your code. Try the following code and tell me if an error exists.
Recipe.find().then(doc3 => {
data = doc3;
for (var i = 0; i < data.length; i++) {
data[i]['categories'].forEach((item, index) => {
Recipecat.findOne({_id: item}).then(result => {
item = result.name;
catsObjectId.push(item);
});
})
}
console.log(catsObjectId);
})
.catch(err => {
console.log(err);
});
Recently ran into a similar problem. Fix for me was to replace the forEach loop with a simple for loop. It turned out, that the forEach loop is not bothering about async-await, but the for loop is.
Here is my code snippet:
let orders = await order_db.find({ profileID: req.body.id }).exec();
let final_orders = [];
for(let i=0; i<orders.length; i++){
let order = orders[i];
if (order.shopID != null) {
let shop = await shop_db.find({ _id: order.shopID }).exec();
let shopName = shop[0].name;
let shopEmail = shop[0].email;
let shopAddress = shop[0].address;
let shopPhone = shop[0].phone;
let updated_order = { ...order._doc, shopName, shopEmail, shopAddress, shopPhone };
final_orders.push(updated_order);
}
else {
let shopName = "";
let shopEmail = "";
let shopPhone = "";
let shopAddress = "";
let updated_order = { ...order._doc, shopName, shopEmail, shopAddress, shopPhone };
final_orders.push(updated_order);
}
};
I was learning react and doing some axios api call with an array. I did a code on gathering data through coinmarketcap api to learn.
So, my intention was to get the prices from the api with a hardcoded array of cryptocurrency ids and push them into an array of prices. But I ran into a problem with the prices array, as the prices were all jumbled up. I was supposed to get an array in this order
[bitcoinprice, ethereumprice, stellarprice, rippleprice]
but when I ran it in the browser, the prices came randomly and not in this order, sometimes I got my order, sometimes it didn't. I used a button which onClick called the getPrice method. Does anyone know what went wrong with my code? Thanks!
constructor(){
super();
this.state = {
cryptos:["bitcoin","ethereum","stellar","ripple"],
prices:[]
};
this.getPrice = this.getPrice.bind(this);
}
getPrice(){
const cryptos = this.state.cryptos;
console.log(cryptos);
for (var i = 0; i < cryptos.length; i++){
const cryptoUrl = 'https://api.coinmarketcap.com/v1/ticker/' + cryptos[i];
axios.get(cryptoUrl)
.then((response) => {
const data = response.data[0];
console.log(data.price_usd);
this.state.prices.push(data.price_usd);
console.log(this.state.prices);
})
.catch((error) => {
console.log(error);
});
}
}
If you want to receive the data in the order of the asynchronous calls you make, you can use Promise.all, that waits until all the promises of an array get executed and are resolved, returning the values in the order they were executed.
const cryptos = ['bitcoin', 'ethereum', 'stellar', 'ripple'];
const arr = [];
for (var i = 0; i < cryptos.length; i++){
const cryptoUrl = 'https://api.coinmarketcap.com/v1/ticker/' + cryptos[i];
arr.push(axios.get(cryptoUrl));
}
Promise.all(arr).then((response) =>
response.map(res => console.log(res.data[0].name, res.data[0].price_usd))
).catch((err) => console.log(err));
You could use a closure in the for loop to capture the value of i and use it as the index once the data is returned rather than using push:
getPrice(){
const cryptos = this.state.cryptos;
console.log(cryptos);
for (var i = 0; i < cryptos.length; i++) {
const cryptoUrl = 'https://api.coinmarketcap.com/v1/ticker/' + cryptos[i];
(function (x) {
axios.get(cryptoUrl)
.then((response) => {
const data = response.data[0];
console.log(data.price_usd);
var newPrices = this.state.prices;
newPrices[x] = data.price_usd;
this.setState({prices: newPrices});
console.log(this.state.prices);
})
.catch((error) => {
console.log(error);
});
})(i);
}
}