I do a http call to get an Array with objs. And now I want to call for each objs that return me an ID another http call. After all I want to have one observable result.
So far I managed to get for each index a http call. The problem instead of one result I got multiple.
getStats(tag: string) {
return this.service.getClanByClanTag(tag)
.map(clan => {
return clan.memberList; //the arr that return the ID's
})
.switchMap((member: PlayerByMemberListType[]) => {
return member; // singleObj of the arr
})
.concatMap((singleMember) => {
return this.service.getPlayerData(singleMember.tag).map(player => {
//push data to the new arr which should return only one time
this.newArr.push({
tag: singleMember.tag,
name: singleMember.name,
warStars: player.warStars,
trophiesNightBase: singleMember.versusTrophies
});
return this.newArr;
});
});
}
This is what the console prints out after subscribing to it:
Array [ {…} ]
Array [ {…}, {…} ]
Array(3) [ {…}, {…}, {…} ]
Array(4) [ {…}, {…}, {…}, {…} ]
Array(5) [ {…}, {…}, {…}, {…}, {…} ]
...
I know I need some kind of Observable.forkJoin but I don't know how integrate it in the code.
Try something like this:
this.service.getClanByClanTag(tag)
.mergeMap(clan => clan.memberList)
.mergeMap(
member => this.service.getPlayerData(member.tag), // supposedly this returns an observable
(member, player) => ({
tag: member.tag,
name: member.name,
warStars: player.warStars,
trophiesNightBase: member.versusTrophies
})
)
.toArray()
So basically what you want to achieve is this.
Get the clan info
Using clan info from step 1, get the memberList in the clan
For each member inside the memberList, get the players
You will need to think of a way to preserve the info at step2 when before switchMap in step3. Usually we will use a Subject, but in the case if you do not want to, simply map the Observable to preserve the data:
getStats(tag: string) {
return this.service.getClanByClanTag(tag)
.map(clan => {
return clan.memberList; //the arr that return the ID's
})
.switchMap((memberList: PlayerByMemberListType[]) => {
//note that the following map is a function of javascript array, not Observable
//it returns an array
let arrayOfObservables = memberList.map(singleMember => {
this.service.getPlayerData(singleMember.tag)
//map the data so as to preserve the data of singleMember
//by creating a new object, using Object.assign
.map(playerData => {
return Object.assign({memberData: singleMember}, playerData,)
});
})
return Observable.forkJoin(arrayOfObservables);
})
.map(players => {
//players is an array of Object that is the format of {memberData:singleMember, playerData:player)
//perform Object destructuring method
return players.map(({memberData,playerData}) => {
return {
tag: memberData.tag,
name: memberData.name,
warStars: playerData.warStars,
trophiesNightBase: memberData.versusTrophies
}
})
})
}
Related
I'm using autoCompletejs and want to fetch my list of keys from my backend, but am getting the following error:
autoComplete.js:119 Uncaught (in promise) TypeError: Invalid attempt to iterate non-iterable instance. In order to be iterable, non-array objects must have a Symbol.iterator method.
My "data" code:
data: {
src: async () => {
try {
// Loading placeholder text
document.getElementById("autoComplete").setAttribute("placeholder", "Loading...");
// Fetch External Data Source
const source = await fetch("/suppliers");
const data = await source.json();
console.log(data)
// Post Loading placeholder text
document.getElementById("autoComplete").setAttribute("placeholder", autoCompleteJS.placeHolder);
// Returns Fetched data
return data;
} catch (error) {
return error;
}
},
keys: fetch("/keys").then(response => response.json()) //.then(jsonData => console.log(jsonData)
),
cache:true
},
Values logged:
jsonData:
(10) ['263897', '186000', '146294', '278717', '189906', '246548', '130111', '157960', '947854', '138753']
0: "263897"
1: "186000"
2: "146294"
3: "278717"
4: "189906"
5: "246548"
6: "130111"
7: "157960"
8: "947854"
9: "138753"
length: 10
[[Prototype]]: Array(0)
data:
0: {100058: 'Supplier1', 100211: 'Supplier2', 100476: 'Supplier3', 100986: 'Supplier4' …}
length: 1
[[Prototype]]: Array(0)
Looks like your /suppliers endpoint returns a singleton array (an array with only one item in it) with an object of id / label pairs.
First, singleton arrays are useless. I'd change the backend to return an array of objects but in the meantime, you could map the result to something more useful
data: {
src: async (query) => {
const res = await fetch("/suppliers");
if (!res.ok) {
throw new Error(`${res.status}: ${await res.text()}`);
}
// get the first / only array item
const [data] = await res.json();
// map to an array of objects with id & label properties
return Object.entries(data).map(([ id, label ]) => ({ id, label }));
},
keys: ["label"],
}
I am not into javascript and angular, currently working on some UI task and has workable/little knowledge of javascript
what i want, base on a key field filter out the objects.
currently what code is doing, hitting a api, which is storing some result in an array. this array result are in object (promise object) i guess, and i need to set out a filter condition on this object base on some value.
but for me issue is, object does not have key same as response, it store. data like this
Array(5) [ {…}, {…}, {…}, {…}, {…} ]
0: Object { _isScalar: false, source: {…}, operator: {…} }
1: Object { _isScalar: false, source: {…}, operator: {…} }
2: Object { _isScalar: false, source: {…}, operator: {…} }
3: Object { _isScalar: false, source: {…}, operator: {…} }
4: Object { _isScalar: false, source: {…}, operator: {…} }
length: 5
so even if i iterate through each object i cant find the key which is my filter criteria.
below is code related to this
getSomeThing(
name: string,
dcId: any
): Promise<any[]> {
const url = `<url>`;
return this.http.get(url, { headers: this.sharedService.getHeaders() })
.toPromise()
.then(response => this.getSomeThingOneByOne(name, dcId, response.json()))
.catch(error => this.sharedService.handleError(error));
}
private getSomeThingOneByOne(name, dcId, someIds): Promise<any> {
const someObservables = [];
someIds.forEach(some => someObservables.push(this.getsomethingObservable(name, dcid, some)));
return Observable.forkJoin(someObservables)
.toPromise()
.catch(error => this.sharedService.handleError(error));
}
getsomethingObservable(
name: string,
dcId: any,
someId: any
): Observable<any> {
const url = `<url>`;
return this.http.get(url, { headers: this.sharedService.getHeaders() })
.map((response: Response) => {
const vm = response.json();
return vm;
})
.catch((error: any) => Observable.throw(this.sharedService.handleError(error)));
}
note i have change the name of function and variable.,
now here, getsomeThing call an api which return a list of ids and pass it to getSomethingOneByOne as array ie [1,2,3,4,5] , and this function getSomethingOneByOne call getsomethingObservable to get the data related to that id.
what i want is once i recied the data i should check a certain value in it or in getsomethingObservable check the value it self for that key and filter out the result.
but due since i am not able to read the data in array someObservables in function getSomeThingOneByOne raw i cant add filter condition there.
need help here as it requires knowledge of promise, obserable, angular and typescript which i lack.
just solved this by taking reference from #sonusindhu suggestion and taking reference from here too.
i had made changes on getSomeThingOneByOne as below and it worked
private getSomeThingOneByOne(name, dcId, someIds): Promise<any> {
const someObservables = [];
someIds.forEach(some => someObservables.push(this.getsomethingObservable(name, dcid, some)));
return Observable.forkJoin(someObservables)
.toPromise()
.then(response => {
var data = response.filter(val=>val[<somekey>]!='<a certain value>');
return data;
})
.catch(error => this.sharedService.handleError(error));
}
I'm storing the API in the global array (tempdata) and when I print tempdata I'll get the response stores in the array but the problem here is when I try to get the specific array location like tempdata[0] or tempdata[1] it gives me undefined.
error if I use console.log(tempdata[0]);
printed array on console:
tempdataaa
[]
0: {values: {…}, series: Array(1), truncated: false}
1: {values: {…}, series: Array(1), truncated: false}
length: 2
__proto__: Array(0)
component code:
let tempdata: any =[];
for (let a = 0; a < this.checkDevice.length; a++) {
this.httpRequestService.ChartData(this.dateTo, this.checkDevice[a], this.mergeactive[0][a],
this.dateFrom).then(mresp => {
tempdata.push(mresp) ;
});
}
That's because you're trying to access object like an array. To do that you should map every object and push it into the array like this:
.then(mresp => {
const mapped = Object.keys(mresp).map(key => mresp[key]);
//now you can log map element and access it with indexes
});
How do I change the values of my object to use the fixed values as I have done with my console.log?
fetchData = () => {
axios.get(fullAPI).then(res => {
const apiResponse = res.data
apiResponse.forEach(employee => console.log('test', employee.value.toFixed(2)))
apiResponse.forEach(employee => employee.value.toFixed(2))
console.log('raw data', apiResponse)
this.setState({
employeeData: apiResponse,
})
})
}
test 4.41
test 5.00
test 6.16
test 0.79
raw data
(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
0: {name: "Animals", value: 4.41361634}
1: {name: "Environment", value: 5.004498622999998}
The error with your code is that you are just formatting the value number and returning it to forEach (that does nothing with this information).
You must modify the original object.
fetchData = () => {
axios.get(fullAPI).then(res => {
const apiResponse = res.data;
apiResponse.forEach(employee => {
employee.value = employee.value.toFixed(2); // Note we are attributing the fixed value to the value itself, modifying the object
});
console.log('raw data', apiResponse)
this.setState({
employeeData: apiResponse
})
})
}
Note that this works because JavaScript always use a reference for objects (like a pointer in C), so even without returning the object, you are changing it properties and it will reflect on the original array.
IMO is more readable for non experienced (and experienced as well) programmers using Array.map() instead, since it will make clear that you are modifying the object and updating the array:
fetchData = () => {
axios.get(fullAPI).then(res => {
const apiResponse = res.data;
apiResponse.map(employee => {
employee.value = employee.value.toFixed(2)
return employee;
});
console.log('raw data', apiResponse)
this.setState({
employeeData: apiResponse
})
})
}
Both codes will do the same, probably no performance differences, just different readability.
You need to set employee.value to the desired value. Additionally, I recommend not using the implicit return syntax and use braces for readability.
apiResponse.forEach(employee => {
employee.value = employee.value.toFixed(2);
});
When I get several documents from a collection, the result is only an array with each doc data.
firestore.collection("categories").valueChanges().subscribe(data => {
console.log(data);
// result will be: [{…}, {…}, {…}]
};
How can I get the name of each doc?
The ideal result would look like this:
{"docname1": {…}, "docname2": {…}, "docname3": {…}}
// Print each document
db.collection("categories")
.onSnapshot((querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(doc.data()); // For data inside doc
console.log(doc.id); // For doc name
}
}
When you need to access additional metadata like the key of your Document, you can use the snapshotChanges() streaming method.
firestore.collection("categories").valueChanges().map(document => {
return document(a => {
const data = a.payload.doc.data();//Here is your content
const id = a.payload.doc.id;//Here is the key of your document
return { id, ...data };
});
You can review the documentation for further explanation and example
This is dart code:
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('your collection')
.snapshots(), // path to collection of documents that is listened to as a stream
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
return ListView(
children: snapshot.data.docs.map((DocumentSnapshot doc) { // get document data
return yourWidget( // return a widget for every document in the collection
docId: doc.id // get document name
);
}).toList(), // casts to list for passing to children parameter
);
},
),