Javascript returning Null value in object - javascript

I am using the following code to call an API and return results:
api.jobs.all(function(response) {
const obj = response.data.map(function(item) {
return [item.id, item.billed.amountString];
});
});
With the following JSON:
{
"data": [
{
"id": 2090170,
"deadline": null,
"jobId": {
"id": 1644
},
"billed": {
"amountString": 200,
"currencyType": "CAD"
}
},
{
"id": 2090171,
"deadline": null,
"jobId": {
"id": 1645
},
"billed": {
"amountString": 400,
"currencyType": "USD"
}
}]}
The code is working fine, for the most part I am getting back good results, with the exception of: billed.amountString
I keep getting the following error:
TypeError: Cannot read property 'amountString' of null
Can anyone see why this would be returning null?
Also, is there a way in which I could loop through the API call and force it to do the following:
If .amountString === null, .amountString = "";

var response = {
"data": [
{
"id": 2090170,
"deadline": null,
"jobId": {
"id": 1644
},
"billed": {
"amountString": 200,
"currencyType": "CAD"
}
},
{
"id": 2090171,
"deadline": null,
"jobId": {
"id": 1645
},
"billed": {
"amountString": 400,
"currencyType": "USD"
}
}]};
const obj = (response.data).map(function(item) {
return [item.id, item.billed.amountString];
});
console.log(obj);

You could use the library lodash. The lodash method get can be used to try and access an object field. If it does not exist you can specify a default return value. See https://lodash.com/ .
// This will try to access item.billed.amountString
// If an item does not exist anywhere along the way
// it will return the default.
// _.get( OBJECT, PATH, DEFAULT )
_.get(item, ['billed', 'amountString'], '')

Related

Filter on objects in computed property returning is not a function vue 3 - Which is the right approach

So at the beginning I was trying to filter just using filter, but then I realised that was wrong when getting the error "objectColours.filter is not a function" because was an object. So tried an approach that I used before so my computed property was looking like:
colorByModel() {
var objectColours = this.colours.allCatalogModelColours; // a getter that contains the object
const result = objectColours.filter((item) => {
return item.model.findIndex((m) => m.modelName === "ModelThree") > -1;
});
return result;
}
So I basically I wanted to return all the values(colourSerie by example) filtered by model. After made the change I was getting the error "findIndex is not a function". This is the current structure of my object:
[
{
"id": "59035506",
"colourSerie": [
{
"colorName": " Green",
"colourHex": {
"hex": "#C3E300"
}
},
{
"colorName": " Blue",
"colourHex": {
"hex": "#061A38"
}
},
{
"colorName": " Grey",
"colourHex": {
"hex": "#45433C"
}
}
],
"model": {
"id": "48355529",
"modelName": "ModelOne"
}
},
{
"id": "59035507",
"colourSerie": [
{
"colorName": " Green",
"colourHex": {
"hex": "#C3E300"
}
}
],
"model": {
"id": "48355529",
"modelName": "ModelTwo"
}
},
{
"id": "59035508",
"colourSerie": [
{
"colorName": " Blue",
"colourHex": {
"hex": "#061A38"
}
}
],
"model": {
"id": "48355529",
"modelName": "ModelThree"
}
}
]
For me is not clear, I might think is because is expecting a structure like [{}] instead of {} ? If that's the case, why would be the right approach? And if I am wrong, can you please explain me the correct way to filter an object.
In addition, after this result I also tried, but with similar result. I this case I got the next error "Cannot read properties of undefined (reading 'any')". I tried also with some instead, but same message.
var objectColours = this.colours.allCatalogModelColours; // a getter that contains the object
var result = {}
Object.keys(objectColours).forEach(key => {
const item = objectColours[key]
if (item.model.any(m => m.modelName === "ModelThree")) {
result[key] = item
}
})
return result;
Thank you in advance for your help and time.
It seems you are mixing functionalities of objects and arrays. filter, find, findIndex are array methods and you are trying some of them on your array items, which are objects.
If I understand your problem correctly, you can just use this:
colorByModel() {
var objectColours = this.colours.allCatalogModelColours; // a getter that contains the object
return objectColours.find((item) => item.model.modelName === "ModelThree");
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find

Push firestore snapshot return into an array

I'm having trouble copying the mapped return of a onSnapshot listener from firebase inside an array (But very IMPORTANT : I don't want to use the state syntax):
useEffect(() => {
let copyChats = [];
const unsubscribe = db.collection('chats').onSnapshot(snapshot =>
(
console.log(snapshot.docs.map(doc => ({
id: doc.id,
data: doc.data()
}
)))
)
)
return unsubscribe;
}, [])
This code returns correctly the content in the console:
Array [
Object {
"data": Object {
"chatName": "presidentielle",
},
"id": "8DRKgDCW54Zt4yf1anoR",
},
Object {
"data": Object {
"chatName": "titi",
},
"id": "9MudmtUfi9nGiWMpwHGk",
},
Object {
"data": Object {
"chatName": "essais",
},
"id": "Hi5a8FpDaf7EwwwpmOXn",
},
Object {
"data": Object {
"chatName": "titi",
},
"id": "ZnX3jaRkrJN4oiNCC3un",
},
Object {
"data": Object {
"chatName": "essais",
},
"id": "dPruNs46X0jbhr0sL7xk",
},
Object {
"data": Object {
"chatName": "test chat",
},
"id": "vxs7RdSBER83TOCPHko1",
},
]
Array [
Object {
"data": Object {
"chatName": "presidentielle",
},
"id": "8DRKgDCW54Zt4yf1anoR",
},
Object {
"data": Object {
"chatName": "titi",
},
"id": "9MudmtUfi9nGiWMpwHGk",
},
Object {
"data": Object {
"chatName": "essais",
},
"id": "Hi5a8FpDaf7EwwwpmOXn",
},
Object {
"data": Object {
"chatName": "titi",
},
"id": "ZnX3jaRkrJN4oiNCC3un",
},
Object {
"data": Object {
"chatName": "essais",
},
"id": "dPruNs46X0jbhr0sL7xk",
},
Object {
"data": Object {
"chatName": "test chat",
},
"id": "vxs7RdSBER83TOCPHko1",
},
]
But what I want to do is to copy this exact data model in an the array copyChats, and I can't do it. I've tried all kinds of spread and push syntax.
useEffect(() => {
let copyChats = [];
const unsubscribe = db.collection('chats').onSnapshot(snapshot =>
(
copyChats = snapshot.docs.map(doc => ({
id: doc.id,
data: doc.data()
}
))
)
)
console.log(copyChats);
return unsubscribe;
}, [])
The only thing I'm able to get is an empty array !like the code above which give me this return:
Array []
If you have any Idea. Thank you..
The problem is not where you access the array, but when you access it. If you run the code in a debugger, or add a console.log("Hello") inside he call, you'll see that the console.log(copyChats) actually runs before copyChats = snapshot.docs.map ever executes. So that explains why the array is empty: it hasn't been populated yet.
I don't want to use the state syntax
React's state is precisely the mechanism to pass data that is asynchronously loaded to the UI. If you want to render the data from the database in the UI, using state is the correct approach.
Also see:
React native call function in text element
React Not Updating Render After SetState
Using Map in React with firebase

Recursive delete from ngrx store

I use Angular 10 and library ngrx store and I don't understand how to recursively delete from ngrx store. In the store I have a nested array of objects. How do I recursively delete an object by id? When I try to delete using splice and function I got an error:
Cannot assign to read only property '0' of object '[object Array]'
What am I doing wrong?
Data Example:
[
{
"id": 414,
"name": "mail.ru",
"is_main": true,
"subdomains": [
{
"id": 423,
"name": "en.mail.ru",
"is_main": false,
"subdomains": [
{
"id": 429,
"name": "gw.mail1.ru",
"is_main": false,
"subdomains": [
{
"id": 426,
"name": "gw.mail3.ru",
"is_main": false,
"subdomains": []
}
]
}
]
},
{
"id": 425,
"name": "gw.mail.ru",
"is_main": false,
"subdomains": []
}
]
}
]
Store reducer:
case UserInfoActionTypes.UPDATE_DOMAINS_LIST: {
return {
...state,
domainsInfo: deleteItems(state.domainsInfo, [parseInt(action.payload.id, 10)]),
errorMessage: null
};
}`
My Recursive function:
export function deleteItems(array, ids) {
let i = array.length;
while (i--) {
if (ids.indexOf(array[i].id) !== -1) {
array.splice(i, 1);
continue;
}
array[i].subdomains && deleteItems(array[i].subdomains, ids);
}
return array;
}
So basically you need to create new objects for everything in state, deleteItems() could look like this:
export function deleteItems(array, ids) {
let result = [];
for(item of array) {
if (!ids.includes(item.id)) {
result.push({
...item,
subdomains: deleteItems(item.subdomains, ids),
})
}
}
return result;
}
I'm not too familiar with ngrx, maybe I missed a detail.

PokeAPI + Angular: How to get pokemon's evolution chain

I am an Angular novice and am learning a little by trying to pull the evolution chain for each pokemon using pokeapi but having a difficult time because of deep nesting.
A typical response object is returned like this:
{
"baby_trigger_item": null,
"id": 2,
"chain": {
"evolution_details": [],
"evolves_to": [
{
"evolution_details": [
{
"min_level": 16,
"min_beauty": null,
"time_of_day": "",
"gender": null,
"relative_physical_stats": null,
"needs_overworld_rain": false,
"turn_upside_down": false,
"item": null,
"trigger": {
"url": "http://pokeapi.co/api/v2/evolution-trigger/1/",
"name": "level-up"
},
"known_move_type": null,
"min_affection": null,
"party_type": null,
"trade_species": null,
"party_species": null,
"min_happiness": null,
"held_item": null,
"known_move": null,
"location": null
}
],
"evolves_to": [
{
"evolution_details": [
{
"min_level": 36,
"min_beauty": null,
"time_of_day": "",
"gender": null,
"relative_physical_stats": null,
"needs_overworld_rain": false,
"turn_upside_down": false,
"item": null,
"trigger": {
"url": "http://pokeapi.co/api/v2/evolution-trigger/1/",
"name": "level-up"
},
"known_move_type": null,
"min_affection": null,
"party_type": null,
"trade_species": null,
"party_species": null,
"min_happiness": null,
"held_item": null,
"known_move": null,
"location": null
}
],
"evolves_to": [],
"is_baby": false,
"species": {
"url": "http://pokeapi.co/api/v2/pokemon-species/6/",
"name": "charizard"
}
}
],
"is_baby": false,
"species": {
"url": "http://pokeapi.co/api/v2/pokemon-species/5/",
"name": "charmeleon"
}
}
],
"is_baby": false,
"species": {
"url": "http://pokeapi.co/api/v2/pokemon-species/4/",
"name": "charmander"
}
}
}
I have to get to evolves_to property, and grab species.name as well as evolution_details.min_level and evolution_details.trigger.name, and evolution_details.item if not null
But as you can see, the evolves_to property, itself contains another evolves_to nested inside, which has another nested inside
This is my sad attempt (after http.get) and I'm just stuck now.
var evoObject = response.data;
function loopEvo(obj){
angular.forEach(obj, function(value, key, object){
if (key == 'evolves_to' && value != []) {
//from here I can get top level data, but...
}
});
}
loopEvo(evoObject.chain);
I don't know how to recursively dive into objects and continually grab data, can anyone provide any help? I would love to use this as a great learning opportunity in traversing complex json objects.
You could always just avoid using Angular and stick with plain JS to build out your evolution chain... try giving this a go, it was based on your angular for loop. This should leave you with an array (evoChain) of the objects containing the data you are looking for ordered from first evolution at 0 index to last evolution at the last index.
var evoChain = [];
var evoData = response.data.chain;
do {
var evoDetails = evoData['evolution_details'][0];
evoChain.push({
"species_name": evoData.species.name,
"min_level": !evoDetails ? 1 : evoDetails.min_level,
"trigger_name": !evoDetails ? null : evoDetails.trigger.name,
"item": !evoDetails ? null : evoDetails.item
});
evoData = evoData['evolves_to'][0];
} while (!!evoData && evoData.hasOwnProperty('evolves_to'));
In your sample case above the resulting array should appear as follows:
[{
"species_name": "charmander",
"min_level": 1,
"trigger_name": null,
"item": null
}, {
"species_name": "charmeleon",
"min_level": 16,
"trigger_name": "level-up",
"item": null
}, {
"species_name": "charizard",
"min_level": 36,
"trigger_name": "level-up",
"item": null
}]
The approved answer above does not work if there are multiple evolutions such as Eevee or Snorunt. That will only return the first evolution e.g. Vaporeon
The following checks number of evolutions and runs through them all.
let evoChain = [];
let evoData = chain.chain;
do {
let numberOfEvolutions = evoData['evolves_to'].length;
evoChain.push({
"species_name": evoData .species.name,
"min_level": !evoData ? 1 : evoData .min_level,
"trigger_name": !evoData ? null : evoData .trigger.name,
"item": !evoData ? null : evoData .item
});
if(numberOfEvolutions > 1) {
for (let i = 1;i < numberOfEvolutions; i++) {
evoChain.push({
"species_name": evoData.evolves_to[i].species.name,
"min_level": !evoData.evolves_to[i]? 1 : evoData.evolves_to[i].min_level,
"trigger_name": !evoData.evolves_to[i]? null : evoData.evolves_to[i].trigger.name,
"item": !evoData.evolves_to[i]? null : evoData.evolves_to[i].item
});
}
}
evoData = evoData['evolves_to'][0];
} while (!!evoData && evoData.hasOwnProperty('evolves_to'));
return evoChain;
brandudno is correct: the extra if(numberOfEvolutions) is the more complete approach (THANKS #brandudno! This really helped me solve for ALL the use cases - including eevee!)
I like the use of !!evoData in the while statement now that I took the time to understand it, but it was confusing for me at 1st, so I made a minor modification that still works and may be easier for other new developers (continues until evoData becomes undefined).
Lastly, I made a minor change in case others also prefer to use the . annotation (evoData.evolves_tovs.evoData['evolves_to']`) to take advantage of autocomplete, etc.
let evoChain = [];
let evoData = chain.chain;
do {
let numberOfEvolutions = evoData.evolves_to.length;
evoChain.push({
"species_name": evoData .species.name,
"min_level": !evoData ? 1 : evoData .min_level,
"trigger_name": !evoData ? null : evoData .trigger.name,
"item": !evoData ? null : evoData .item
});
if(numberOfEvolutions > 1) {
for (let i = 1;i < numberOfEvolutions; i++) {
evoChain.push({
"species_name": evoData.evolves_to[i].species.name,
"min_level": !evoData.evolves_to[i]? 1 : evoData.evolves_to[i].min_level,
"trigger_name": !evoData.evolves_to[i]? null : evoData.evolves_to[i].trigger.name,
"item": !evoData.evolves_to[i]? null : evoData.evolves_to[i].item
});
}
}
evoData = evoData.evolves_to[0];
} while (evoData != undefined && evoData.hasOwnProperty('evolves_to'));
return evoChain;
I am using a recursive function to solve this.
Here's how it goes with plain JavaScript.
let evoChain = [];
function getEvo(arr) {
if (arr[0].evolves_to.length > 0) {
evoChain.push(arr[0].species.name);
getEvo(arr[0].evolves_to);
} else {
evoChain.push(arr[0].species.name);
return 0;
}
}
getEvo([data.chain]);```

combine json array into one json array by id

I want to merge item and purchases array of json into one by matching their property value.
Here's the source :
{
"item": [
{
"invoiceId": 1
},
{
"invoiceId": 2
},
{
"invoiceId": 3
}
],
"purchase": [
{
"id": "1",
"date": "12/1/2014"
},
{
"id": "2",
"date": "12/1/2014"
},
{
"id": "3",
"date": "12/1/2014"
}
]
}
I want to produce something like this :
{
"combined": [
{
"invoiceId": 1,
"id": "1",
"date": "12/1/2014"
},
{
"invoiceId": 2,
"id": "2",
"date": "12/1/2014"
},
{
"invoiceId": 3,
"id": "3",
"date": "12/1/2014"
}
]
}
How can I match the item.invoiceId with purchase.id?
Solution
assuming obj is your object
var new_obj = {combined:[]};
obj["purchase"].forEach(function(a) {
obj["item"].forEach(function(b){
if (+b["invoiceId"]===(+a["id"])) {
a["invoiceId"] = b["invoiceId"] || 0;//WILL MAKE INVOICEID 0 IF IT IS NOT DEFINE. CHANGE 0 TO YOUR NEEDS
new_obj.combined.push(a);
}
});
});
How it works
The first .forEach() loops through obj.purchase. Then we loop through obj.item To check if their is a matching invoiceId (if you don't need to make sure their is a matching invoiceId, use the alternate code). Then, we simply add a new value to the new_obj
The result (copied from console) is:
{
"combined":[
{
"id":"1",
"date":"12/1/2014",
"invoiceId":1
},
{
"id":"2",
"date":"12/1/2014",
"invoiceId":2
},
{
"id":"3",
"date":"12/1/2014",
"invoiceId":3
}
]
}
Alternative Code
Use this if you don't need to make sure, invoiceId is there
var new_obj = {combined:[]};
obj["purchase"].forEach(function(a){a["invoiceId"]=a["id"];new_obj.combined.push(a);});
One way of achieving what you want will be
var result = {};
var getMatchingPurchase = function(invoiceId) {
return data.purchase.filter(function(purchase) {
return invoiceId == purchase.id;
})[0];
};
result.combined = data.item.map(function(invoice) {
var purchase = getMatchingPurchase(invoice.invoiceId);
return {
invoiceId: invoice.invoiceId,
id: purchase.id,
date: purchase.date
};
});
console.log(result);
It will print like bellow
{ combined:
[ { invoiceId: 1, id: '1', date: '12/1/2014' },
{ invoiceId: 2, id: '2', date: '12/1/2014' },
{ invoiceId: 3, id: '3', date: '12/1/2014' } ] }
Note:- I'm using map and filter functions which are not supported in IE8. If you want to use in IE8 you have to use for loop.
If you have to support old browsers like IE8 (poor guy...), note that the native forEach might not be supported, in this case you can use lodash for cross-browser compatibility:
function getCombinedResult(source){
var combinedList = [];
_.each(source.item, function(item){
_.each(source.purchase, function(purchase){
if (item['invoiceId'].toString() != purchase['id'].toString()) return;
var combinedItem = _.extend(item, purchase)
combinedList.push(combinedItem);
});
})
return {"combined": combinedList};
}

Categories