autoCompletejs: How to fetch keys from backend? - javascript

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"],
}

Related

How to read data from array after promise/observable call

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));
}

Why is one API response returning just an object over and array of objects? [duplicate]

This question already has answers here:
Why is "forEach not a function" for this object?
(4 answers)
Closed 1 year ago.
Currently my Api is returning an object instead of an object within an array, however I can't seem to tell the difference between these two routes and why one would return said array of data over the other.
For instance :
router.get('/caseDetail/:id', (req,res) => {
db.caseDetail.findOne({
include : [db.Part,db.Fault,db.Disposition,db.Contact,db.Site]
}).then((response) => {
res.json(response);
}).catch((error) => {
console.table([stack.error,stack.id,error])
})
})
The above route returns an array of data while the following returns just an object
router.get('/caseDetail/:caseName', (req,res) => {
db.caseDetail.findAll({
include : [db.Part,db.Fault,db.Disposition,db.Contact,db.Site],
where : {
caseName:{
[Op.like]: req.params.caseName}
}
}).then((response) => {
console.log(response);
res.json(response)
}).catch((error) => {
console.log(error);
})
})
-------------------------- For context----------------------------
I've enacted this method multiple times, even in other script files, but I haven't been required to parse data in this manner, is something out of shape here that im over looking? Am I missing a JSON.parse(); here? Github
try {
const items = await axios.get(`/api/caseDetail/:caseName` + caseName);
console.log(items.data);
$tbody.empty()
items.data.forEach((item) => {
console.log(item);
Returned Results
{id: 2, caseName: "1 - Fenway Park - 1", createdAt: "2021-07-27T18:13:55.000Z", updatedAt: "2021-07-27T18:13:55.000Z", UserId: 1, …}
Error Message
TypeError: items.data.forEach is not a function
at callSearchResults (searchInventory.js:29)
If I understand you correctly and this is the result that you receive from your request:
{id: 2, caseName: "1 - Fenway Park - 1", createdAt: "2021-07-27T18:13:55.000Z", updatedAt: "2021-07-27T18:13:55.000Z", UserId: 1, …}
Then you would need to take the entries out of it and iterate over them like so:
for (const [key, value] of Object.entries(items)) {
console.log(`${key}: ${value}`);
}
Or like this:
Object.entries(items).forEach(([key, val]) => console.log(key, val));
forEach() is designed for arrays but you can typecast collections and objects into arrays like this...
var I=[...items];
var D=[...items.data]
Now you should be OK, try...
I.data.forEach((item) => {}
D.forEach(() => {}

How to access properties from a Javascript object?

I have an api call that produces the follow results in the console (after pairing it down using map() ).
{…}
​
CHANGE24HOUR: "$ 11.16"
​
CHANGEDAY: "$ 3.31"
​
CHANGEPCT24HOUR: "6.73"
​
CHANGEPCTDAY: "1.90"
​
FROMSYMBOL: "Ξ"
​
HIGH24HOUR: "$ 183.38"
​
HIGHDAY: "$ 183.38"
However, no matter what I try I can't get at it's properties. The object is called 'coinStats'.
I have tried the following:
coinStats.HIGHDAY = undefined
coinStats['HIGHDAY'] = undefined
coinStats["HIGHDAY"] = undefined
Tried to convert it to an array to see if that would help using
Object.values(coinStats) // Would not work
I am sure the answer is so simplistic. But I am just not sure what it is?
The original raw api results are in the following shape:
(1) […]
​
0: Object { CoinInfo: {…}, RAW: {…}, DISPLAY: {…} }
​
length: 1
​
<prototype>: [
The info I am looking for is in DISPLAY -> USD. I used a map() function to return that sub-object.
The code I am using to fetch the data is essentially the following:
const API = 'https://min-api.cryptocompare.com/data/top/mktcapfull?tsym=USD&page=1';
fetch(API)
.then(results => results.json())
.then(coinData => {
const view = coinData.Data.filter(obj => { return obj.CoinInfo.Name === TRX});
})
const coinFeedAbridged = view.map(item => item.DISPLAY.USD);
const coinStats = coinFeedAbridged[0];
console.dir(coinStats);
I can't access coinStats.HIGHDAY for example... I get 'undefined'.
The fetch call is async, so you can't access the view variable until the call is finished. The code calls the async function and runs the next line, so view variable will be undefined. You have to handle the data inside the fetch callback function, where the data is known.
const API = 'https://min-api.cryptocompare.com/data/top/mktcapfull?tsym=USD&page=1';
fetch(API)
.then(results => results.json())
.then(coinData => {
const view = coinData.Data.filter(obj => {
return obj.CoinInfo.Name === 'TRX'
});
const coinFeedAbridged = view.map(item => item.DISPLAY.USD);
const coinStats = coinFeedAbridged[0];
console.log(coinStats);
})
}
You can test it out in this fiddle. https://jsfiddle.net/gran7ptk/1/
Change line - Add inverted commas to Text "TRX".
const view = coinData.Data.filter(obj => { return obj.CoinInfo.Name
=== TRX});
to
const view = coinData.Data.filter(obj => { return obj.CoinInfo.Name === "TRX"});

Unhandled Rejection (TypeError): Invalid attempt to destructure non-iterable instance

I am trying to learn the map method. If I use this syntax response.data.map(d => I am able to iterate data array and see the results, but if I use this syntax response.data.map(([label, CustomStep]) => {, I am getting the error below:
Unhandled Rejection (TypeError): Invalid attempt to destructure non-iterable instance
Can you tell me how to fix it, so that in future I will fix it myself?
Providing my code snippet below:
axios
.get('http://world/sports/values')
.then(response => {
console.log("sports--->", response.data.map(d => d.customFieldValueName));
//this.setState({ playerRanks: response.data.map(d => d.customFieldValueName) });
// es6 map
//Unhandled Rejection (TypeError): Invalid attempt to destructure non-iterable instance
this.setState({
playerRanks: response.data.map(([label, CustomStep]) => {
label.customFieldValueName
})
})
})
update 1:
hey, I saw in console, data is an array inside that there are so many objects
data: Array(19)
[
{
"customFieldValueCode": "player1",
"customFieldValueName": "player1",
"isActive": "Y"
},
{
"customFieldValueCode": "player 2",
"customFieldValueName": "player 2",
"isActive": "Y"
}
]
EDIT:
Based off the data structure provided you could modify your code to...
axios
.get('http://world/sports/values')
.then(response => {
this.setState({
playerRanks: response.data.map(obj => {
return obj.customFieldValueName
})
})
})
OR
...
response.data.map(({customFieldValueName}) => {
return customFieldValueName;
})
...
OR even...
...
response.data.map(({customFieldValueName}) => customFieldValueName)
...
But this would be my recommended solution to provide type checking on you data and proper error handling...
axios
.get('http://world/sports/values')
.catch(err=> console.log(err))
.then(({data}) => { // Axios always returns an Object, so I can safely 'attempt' to destructure 'data' property
if (data && data.length) { // making sure 'data' does exist, it is an Array and has > 0 elements
this.setState({
playerRanks: data.map(obj => { // Not destructuring here in case obj isn't actually an Object
if (obj && obj.customFieldValueName) return customFieldValueName;
return null;
}).filter(elem=> elem) // BIG-O notation: This sequence is O(2N), as in iterates over the entire Array first with .map(), then iterates over the entire Array again with .filter() to clear out 'null' values
})
}
})
In order to prevent your returned Array above from having a bunch of null elements when they don't conform to our assertions, you can use an Array.reduce() method to 'filter' out any nulls...
axios
.get('http://world/sports/values')
.catch(err=> console.log(err))
.then(({data}) => { // Axios always returns an Object, so I can safely 'attempt' to destructure 'data' property
if (data && data.length) { // making sure 'data' does exist, it is an Array and has > 0 elements
this.setState({
playerRanks: data.reduce((acc,obj) => { // Not destructuring here in case obj isn't actually an Object
if (!obj || !obj.customFieldValueName) return acc; // If it doesn't meet assertions just return the existing accumulator (don't add another element .ie 'null')
return [
...acc, // If it conforms to the assertions the return a new accumulator, by first spreading in all existing elements and the adding the new one (customFieldValueName)
customFieldValueName
]
},[]) // BIG-O notation: This is O(1N) or O(N), as in it will only iterate over the Array one time and the reduce() function will filter out 'null' values at the same time
})
}
})
NOTE:
I also just added .filter(elem=> elem) to the end of my first example, which does the same thing as the new .reduce() functionality, but does this in 1N not 2N operations.
PRE-logged data
Here's how the Array.map() method works...
[1,2].map(element=> {
// element === 1, first iteration,
// element === 2, second iteration
})
Here's how Array destructuring works...
[one, two, ...theRest] = [1,2,3,4,5]
// one === 1 and two === 2 and theRest = [3,4,5]
Here's how Object destructuring works...
{one, three, ...theRest} = {one: 1, two: 2, three: 3, four: 4, five: 5}
// one === 1 and three === 3 and theRest === {two: 2, four: 4, five: 5}
// notice order doesn't matter here (three vs two), but you need to access valid properties from the object you're deetructuring from
So based on the way you function is structured you are making the assumption that the data structure of response.data is...
response.data === [
[
{ customFieldValueName: 'any value' }, // label
{} // CustomStep (this could be any value, not necessarily an Object)
],
[
{ customFieldValueName: 'any value' }, // label
'any value' // CustomStep
]
]
I hope this helps conceptually, but if you'd like a workable solution we will need...
Data structure of response.data. Can you provide result of console.log( JSON.stringify( response.data, null, 5) )
Specific values you are trying to assign to the new this.state.playerRanks Array.
PS: A good way to see Object destructuring in action with your current code is to change...
.then( response => {
To
.then( ({data}) => {
In this case, you should be certain that response.data is an array of arrays, because for each iteration of response.data.map, the function you are providing to the map must receive an array to be able to successfully pull the label and CustomStep values, due to the syntax with which you are destructuring the function parameter.
Imagine data in the following example is the response.data and the parseData function is the function you are passing to the map:
let data = [
[{ customFieldValueName: 'field name' }, { stepData: {} }],
[{ customFieldValueName: 'another field name' }, { stepData: {} }]
];
let parseData = ([label, CustomStep]) => console.log(label.customFieldValueName);
parseData(data[0]); // prints out 'field name'
Otherwise, if response.data is an array of objects, which it seems like it is due to you successfully being able to run response.data.map(d => d.customFieldValueName), you could update your map to this (if you simply want to pull the customFieldValueName value out of the object):
response.data.map(({ customFieldValueName }) => customFieldValueName)

RXJS Observables - http call for each index value and merge result

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
}
})
})
}

Categories