property missing in returned json response / javascript - javascript

Im working in React on some weather widget which displays temp and rain forecast. Im fetching data from OpenWeather and my json response looks like:
//rainy day
0:{
main: {
temp:10}
rain: {
3h: 1000}
}
//sunny day
1:{
main: {
temp:10}
}
the problem is rain.3h property appears in returned response only when it has some data, otherwise its missing. My request looks like:
async getForecast(term) {
const forecastUrl = "http://api.openweathermap.org/data/2.5/forecast?q=" + term + "&appid=" + apiKey + "&lang=us&units=metric&cnt=16";
try {
let response = await fetch(forecastUrl);
if (response.ok) {
let jsonResponse = await response.json();
let forecast = jsonResponse.list.map(
day => {
return {
temp: day.main.temp,
rain: day.rain["3h"], // can't map missed property
}
}
)
return (forecast);
}
} catch (error) {
console.log(error);
}
}
And Im getting error
TypeError: Cannot read property '3h' of undefined.
How may I add default rain:0 when the property is missing from response

You could do a check using ternary operator
let forecast = jsonResponse.list.map(
day => {
return {
temp: day.main.temp,
rain: day.rain?day.rain["3h"] : ''
}
}
)

You should check whether the property exists or not
var obj = {};
obj.temp = day.main.temp;
if (day.hasOwnProperty("rain") && day.rain.hasOwnProperty("3h"))
obj.rain = day.rain["3h"];
return obj;

You can use assignment with double ampersand "&&":
let forecast = jsonResponse.list.map(
day => {
return {
temp: day.main.temp,
rain: day.rain && day.rain["3h"] || 0
}
}
)
This works because if day.rain is undefined then the second part of the boolean expression will not be evaluated, avoiding the cannot read property of undefined error, and the value from the OR will be used for default assignment.

Related

ERROR TypeError: Cannot read property 'push' of undefined, js engine: hermes

I need help filtering an array of objects in a typescript react native project using state to store array values and filter objects in the array
see error I keep getting in the method in question
cannot read property push of undefined
Appearing this way
LOG after item
LOG inside 300
ERROR TypeError: Cannot read property 'push' of undefined, js engine: hermes
see my code below
const handleSearchButtonPressed = () => {
console.log("inside handleSearchButtonPressed")
if (!itemListStore){
return
}
const text = searchText.toLowerCase()
console.log("inside 100")
// array of output objects
// eslint-disable-next-line array-callback-return
const filteredObjs = itemListStore.filter((item) => {
console.log(" 200")
console.log(" item is ")
console.log(item)
console.log(" after item")
const strTitle = JSON.stringify(item.title)
const strDesc = JSON.stringify(item.description)
const strLink = JSON.stringify(item.link)
const itemObj = {
title:strTitle,
description: strDesc,
link: strLink
}
if (strTitle.toLowerCase().match(text)) {
console.log("inside 300")
filteredObjs.push(itemObj)
console.log("inside 400")
}
})
console.log("filteredObjs", filteredObjs)
if (!text || text === "") {
setSearchText("",
)
} else if (!Array.isArray(filteredObjs) && !filteredObjs.length) {
// set no data flag to true so as to render flatlist conditionally
setNoData(true)
} else if (Array.isArray(filteredObjs)) {
setNoData(false)
setItemListStore(filteredObjs)
}
}
You're trying to both filter and map. This can more easily be achieved using either two separate operations or one single reduce.
Two operations...
const filteredObjs = itemListStore
.filter(({ title }) => title.toLowerCase().includes(text))
.map(({ title, description, link }) => ({
title: JSON.stringify(title),
description: JSON.stringify(description),
link: JSON.stringify(link),
}));
One reduce...
const filteredObjs = itemListStore.reduce(
(arr, { title, description, link }) => {
if (title.toLowerCase().includes(text)) {
return [
...arr,
{
title: JSON.stringify(title),
description: JSON.stringify(description),
link: JSON.stringify(link),
},
];
}
return arr;
},
[]
);

How can I access return value of Map from function in Node.js for Cloud Function?

I had read this post How to return values in javascript. But my question is how do we get the Map value if we derived it from asynchronous function like in this code below:
async function exampleToken(profile) {
let response;
const tkAdmin = {
admin: true,
};
const tkInvestors = {
investor: true,
};
if (profile == 1) {
response = {
"token": tkAdmin,
"code": 1,
};
} else if (profile == 2) {
response = {
"token": tkInvestors,
"code": 2,
};
}
return Promise.resolve(response);
}
I want to use the value from this function using this code:
const response = await exampleToken(profile);
// Is this correct:
const code = response.code;
const token = response.token;
// or
const code = response["code"];
const token = response["token"];
Please, help me out. Any tips and trick will be great. Thank you very much for spending time to read this post.
Both are correct in Javascript,
1- Dot property accessor: object. property.
2- Square brackets property access: object['property']
3- Object destructuring: const { property } = object.
This style is named Object Dot Notation access
const code = response.code;
const token = response.token;
and this one is Object Bracket notation access
const code = response["code"];
const token = response["token"];
Read more here

obj is undefined sometimes and works perfectly other times

In the computed propertys I am trying to match an ID received from an API to an array of objects with ID keys also received from an API and retrieve the name key from the matching ID object.
the obj variable occasionally throws an "obj is undefined" error but not consistently.
I think its to do with the IDs been async. Changed the function from arrow to classic function to not mess with the this scope.
data() {
return {
upComing: [],
popular: [],
topRated: [],
playingNow: [],
details: [],
genres: []
}
},
computed: {
genre: function() {
let list = this.upComing[0] ? this.upComing[0].genre_ids[0] : 0
let obj = this.genres.find(function(o) {
return o.id === list
})
return obj.name
}
},
created() {
let self = this
APIService.getUpcoming()
.then(response => {
self.upComing = response.data.results
//console.log(this.upComing)
})
.catch(error => {
console.log(`There was an error: ${error.response}`)
}),
APIService.getGenres()
.then(response => {
this.genres = response.data.genres
//console.log(this.genres)
})
.catch(error => {
console.log(`There was an error: ${error.response}`)
})
}
}
I get this TypeError: "obj is undefined" and this [Vue warn]: Error in render: "TypeError: obj is undefined"
and it throws them each twice. So i have 4 errors in the console but its just these 2 twice with a 1 second delay.
The error is that this.genres is [], so the computed property is computed when your component is mounted, so the result of this.genres.find(.... is undefined (because it can't find something in an empty list).
You can have a default value by using the || notation like this:
let obj = this.genres.find(function(o) {
return o.id === list
}) || { name: 'defaultName'}
This means that if nothing is found on the genres list, you still have a default result, then you can return obj.name with out having the error.
Also note that the genres variable is empty because the computed method is tun before your promise is resolved, and it runs again after you update that variable
You're most likely right about the issue being the async stuff, couldn't you just safeguard against undefined by doing something like this:
computed: {
genre: function() {
let list = this.upComing[0] ? this.upComing[0].genre_ids[0] : 0
let obj = this.genres.find(function(o) {
return o.id === list
})
return obj ? obj.name : '' // or whatever should be the default
}
},

How can you destructure an array of objects in Javascript into two predefined variables in ES6?

I have an array containing one object of this form :
Array = [ { type: type, message: message } ]
I keep getting ESLint errors asking me to use object destructuring and array destructuring.
Currently my code looks like this :
let type=null;
let message=null;
if (data.length > 0) {
({ type, message } = data[0]);
}
So far this works and my variables are assigned correctly, however I am still getting the "Use array destructuring" message from ESLint.
Any help with this would be appreciated. Thank you
You can destructure the array:
let type=null;
let message=null;
if (data.length > 0) {
[{ type, message }] = data;
}
The code above is a shorter version of:
[ firstElement ] = data; // array destructruring
({ type, message } = firstElement); // object destructuring
Faly's way is good. You can also use default values when destructuring:
function test(label, data) {
// 1 -----------------------------vvvvv
let [{type = null, message = null} = {}] = data;
// 2 -----^^^^^^^---------^^^^^^^
console.log(label, type, message);
}
test("test1: ", []);
test("test2: ", [{type: "t"}]);
test("test3: ", [{type: "t", message: "m"}]);
That works because if data.length is 0, data[0] is undefined, and so triggers use of the default value {} (1) for the array part of that; within the object part of that, we use null (2) to handle any missing values on the object as well.
EsLint wants you to write
let type = null;
let message = null;
if (data.length > 0) {
[{ type, message }] = data;
}
which destructures the first item of an iterable data into the {type, message} target. (More items are ignored).
I would however recommend to use default values for the empty case:
const [{type, message} = {type:null, message:null}] = data;
or also
const [{type = null, message = null} = {}] = data;

TypeError: map[key].push is not a function

I'm not sure why I'm getting this error as I do have map variable declare in my function:-(
if I run below code :
if (key in map) {
map[key].push(value)
} else {
map[key] = value
}
my output would be like this :
{ url: ['account/43' ],
status: [ '200' ],
headers:
[ 'content-type = application/json',
'content-type = application/text' ],
body: [ '{ name: xyz}' ] }
Instead of that if I run below line of code inside the function :
map[key] = ["headers", "body"].includes(key)? [value] : value
the output would be like below( url/status in string and headers/body in array format) but its not taking multiple value of headers, basically it's replacing the value.
{ url: 'account/43',
status: '200',
headers: [ 'content-type = application/text' ],
body: [ '{ name: xyz }' ] }
I'm trying to achieve kind of both condition( firstly , url.status should be in string format and headers/body should be in array format. secondly headers or body can append/push multiple value like below output:
{url: 'account/43',
status: '200',
headers:
[ 'content-type = application/json',
'content-type = application/text' ],
body: [ '{ name: xyz }' ] }
Here is the actual function
function processFile(content) {
let map = {}
content.forEach(function(node) {
if (node.startsWith("//")) {
key = node.substring(2, node.length-2).toLowerCase().trim()
return
} else {
value = node
}
if (key in map) {
map[key].push(value)
} else {
map[key] = value
}
map[key] = ["headers", "body"].includes(key)? [value] : value
})
return map
}
ERROR
map[key].push(value)
^
TypeError: map[key].push is not a function
This is how you can do it -
function processFile(content) {
let map = {}
content.forEach(function(node) {
if (node.startsWith("//")) {
key = node.substring(2, node.length - 2).toLowerCase().trim()
return
} else {
value = node
}
if (key in map) {
map[key].push(value)
} else {
map[key] = [value];
}
map[key] = ["headers", "body"].includes(key) ? [value] : value
})
return map
}
You've ensured that for headers and body, multiple values are possible. However, based on the error, it's evident that there is a duplicate for some other key as well.
Consider using a function like this:
function processFile(content) {
let key;
return content.reduce(
function (map, node) {
if (node.startsWith('//')) {
key = node.substring(2, node.length-2).toLowerCase().trim();
} else if (key) {
if (map[key]) {
if (map[key].push) {
map[key].push(node);
} else {
throw new Error(`Duplicate key ${key} for scalar value`);
}
} else {
map[key] = node;
}
} else {
throw new Error(`Data encountered without a key: ${node}`);
}
return map;
},
{ headers: [], body: [] }
);
}
This will throw an error when the duplicate key is encountered, giving you a chance to debug it.
Other improvements I made:
Use reduce() instead of forEach().
Initialize the map with an empty array for headers and body, which eliminates the need for a special-case test in your iteration function.
Declared key, which was previously unnecessarily a global.
Data without a previously-seen key causes an error instead of storing the data at map["undefined"].
The method push() belongs to the Array class.
When you do:
map[key] = value
and latter call:
map[key].push(value)
There was no array in map[key]. In order to push an element here we need to store an array in map[key]. To do so, instead of
map[key] = value //element value being stored in map[key]
we need to do something like this:
map[key] = [value] // array containing value in the first position
Initializing and storing the array.

Categories