Pull data from JSON object which may not exist - javascript

Summary
I receive a large JSON object in node--about 10000 lines--from an external API and I'm creating a new, consolidated javascript object with the data I want.
I'm extracting specific key:value pairs from an object, where another key:value pair in the object matches what I'm looking for. The main issue I'm having is that if there is no data for a specific object, that object is not included and the function I wrote to assign the specific data I want to a variable becomes undefined and crashed my node server.
**Example API Response (Abbreviated) **
I commented on the data I'm trying to extract
{
"ApiJSONObject": {
"id": "t4365qewsagsdga4",
"stats": [{
"metadata": {
"key": "kills",
"name": "Kills",
"isReversed": false
},
"value": 6435, //Extract this value and save to specific key in new javascript object
"displayValue": "6,435"
}
],
"segments": [{
"metadata": [{
"key": "segment",
"name": "Segment",
"value": "br.close_solo.season",
"displayValue": null
},
{
"key": "lastPlayedAt",
"name": "Last Played At",
"value": "2018-12-11T16:46:35Z",
"displayValue": "12/11/18 4:46:35 PM"
},
{
"key": "updatedAt",
"name": "Updated At",
"value": "2019-06-10T19:07:00.9143166Z",
"displayValue": "6/10/19 7:07:00 PM"
}
],
"stats": [{
"metadata": {
"key": "kills",
"name": "Kills",
"isReversed": false
},
"value": 1, //extract this value and save to specific key in new javascript object based on metaData[0].value
"displayValue": "1"
},
{
"metadata": {
"key": "matchesPlayed",
"name": "Matches Played",
"isReversed": false
},
"value": 1,
"displayValue": "1"
}
]
}]
}
}
Current Function
I wrote this function, however it breaks my code as stats is undefined if there is no data for that specific statsSegment
function getSegmentStats(statType, playerStats) {
let filteredMetaData = [];
for (var i = 0; i < playerStats.segments.length; i++) {
filteredMetaData = playerStats.segments[i].metadata.filter(
val => val["value"] === statType
);
if (filteredMetaData.length) {
return playerStats.segments[i];
}
}
}
function getStatsFields(value,"br.close_solo.season") {
const stat = statsSegment.stats.find(x => x.metadata.name === value);
return stat.value;
}
const seasonSolo = getSegmentStats("br.close_solo.season", playerStats);
const statsObject = { seasonStats: seasonSolo: getStatsFields("Kills", seasonSolo))

The simplest solution would be to just check if your statsSegment is undefined at the start of your function, but first you need to decide what you do in case it is undefined.
you have few options:
throw an error
return an "errored" value- 0, false, -1- something that will never get returned as stat.value and you'll know for sure it means an error.
emit an event of some sort (don't know the context you're using this).
To perform the check simply add if(statSegment === undefined) at the start of getStatField function.
Also, i'd suggest you look at the docs for that 3rd party API you're using and see what undefined return value even means.
And one last thing- this API might return an empty object (also, check at the docs), so the undefined test will pass but you still won't be able to process the data. You can add an empty object test as well:
if(statSegment === undefined || (Object.entries(statSegment).length === 0 && statSegment.constructor === Object));
if you're using ECMA 7+, or:
if(statSegment === undefined || (Object.keys(statSegment).length === 0 && statSegment.constructor === Object));
if you're using ECMPA 5+.
(for more info about this empty object check go here)

When .find() can't find anything which matches its inner function's criteria, it will by default return undefined. By using the logical OR operator (||) you can set the value of stat to be a default object which always has a value of 0:
function getStatsFields(value,"br.close_solo.season") {
const stat = statsSegment && statsSegment.stats.find(x => x.metadata.name === value) || {value: 0};
return stat.value;
}

let statsSegmentJson = {
"ApiJSONObject": {
"id": "t4365qewsagsdga4",
"stats": [{
"metadata": {
"key": "kills",
"name": "Kills",
"isReversed": false
},
"value": 6435, //Extract this value and save to specific key in new javascript object
"displayValue": "6,435"
}
],
"segments": [{
"metadata": [{
"key": "segment",
"name": "Segment",
"value": "br.close_solo.season",
"displayValue": null
},
{
"key": "lastPlayedAt",
"name": "Last Played At",
"value": "2018-12-11T16:46:35Z",
"displayValue": "12/11/18 4:46:35 PM"
},
{
"key": "updatedAt",
"name": "Updated At",
"value": "2019-06-10T19:07:00.9143166Z",
"displayValue": "6/10/19 7:07:00 PM"
}
],
"stats": [{
"metadata": {
"key": "kills",
"name": "Kills",
"isReversed": false
},
"value": 1, //extract this value and save to specific key in new javascript object based on metaData[0].value
"displayValue": "1"
},
{
"metadata": {
"key": "matchesPlayed",
"name": "Matches Played",
"isReversed": false
},
"value": 1,
"displayValue": "1"
}
]
}]
}
};
let value = 'Kills';
function getStatsFields(value, statsSegment) {
let statsSegmentStr = JSON.stringify(statsSegment);
let statsSegmentObj = JSON.parse(statsSegmentStr);
console.log("statsSegment", statsSegmentObj);
console.log("statsSegment.stats ", statsSegmentObj.ApiJSONObject.stats);
const stat = statsSegmentObj.ApiJSONObject.stats.find(x => x.metadata.name === value);
if (stat) {
return stat.value;
}
}
let result = getStatsFields(value,statsSegmentJson);
console.log(result);

Related

How do you delete an empty array from javascript?

For reference I have zero javascript knowledge or any coding knowledge. I typically just hook up applications via IPASS applications that don't require any coding knowledge. However, I found out I need to inject some javascript into the application in order to avoid an error message.
I have the below JSON record.
I need to get rid of the empty array (sorry... if it is not an array but an object? Like I said, no javascript knowledge).
In the below code essentially what I want is to completely delete this line, because there is nothing inside the brackets and it is causing errors:
"lineitemsdata": []
Full JSON record below for reference
"id": "5399286500",
"properties": {
"state": "AB",
"website": null,
"zip": "T3B5Y9"
},
"createdAt": "2021-02-18T22:13:06.111Z",
"updatedAt": "2021-05-17T14:35:09.540Z",
"archived": false,
"associations": {
"deals": {
"results": [
{
"id": "5230410841",
"type": "company_to_deal"
}
]
}
},
"dealdata": [
{
"id": "5230410841",
"properties": {
"hs_lastmodifieddate": "2021-05-13T14:00:33.101Z",
"hs_object_id": "5230410841",
"hubspot_owner_id": "52200226"
},
"associations": {
"line items": {
"results": [
{
"id": "1468189759",
"type": "deal_to_line_item"
},
{
"id": "1468189760",
"type": "deal_to_line_item",
"lineitemsdata": []
}
]
}
}
}
],
"DealOwner": [
{
"id": "52200226",
"email": "email#email.com",
"firstName": "Bob"
}
],
"NetSuiteCustomerID": 1745
}
Item inside object is called a property. If you (for some reason) have to include the property, but don't want it to have any value you can either set it's value to null or undefined.
I suspect I'm going to get criticized for this, but here is a quick and dirty way of removing this specific problem through string replacement. The 'right' way would be to break down your json into separte objects until you get to the level where the offending object lives, remove it, then rebuild it all back. For what it's worth, here's an alternative to that
let json = {
"id": "5399286500",
"properties": {
"state": "AB",
"website": null,
"zip": "T3B5Y9"
},
"createdAt": "2021-02-18T22:13:06.111Z",
"updatedAt": "2021-05-17T14:35:09.540Z",
"archived": false,
"associations": {
"deals": {
"results": [{
"id": "5230410841",
"type": "company_to_deal"
}]
}
},
"dealdata": [{
"id": "5230410841",
"properties": {
"hs_lastmodifieddate": "2021-05-13T14:00:33.101Z",
"hs_object_id": "5230410841",
"hubspot_owner_id": "52200226"
},
"associations": {
"line items": {
"results": [{
"id": "1468189759",
"type": "deal_to_line_item"
},
{
"id": "1468189760",
"type": "deal_to_line_item",
"lineitemsdata": []
}
]
}
}
}],
"DealOwner": [{
"id": "52200226",
"email": "email#email.com",
"firstName": "Bob"
}],
"NetSuiteCustomerID": 1745
}
json = JSON.stringify(json)
let strstart = json.indexOf('"lineitemsdata":[]');
let commapos = json.lastIndexOf(',', strstart);
json = json.substr(0, commapos) + " " + json.substr(commapos + 1);
json = json.replace('"lineitemsdata":[]', '');
json = JSON.parse(json)
console.log(json)
You can use this to strip any empty lineitems arrays from your json.
Assuming the reference to your record is json
for(dealIdx in json.dealdata) {
for (resultIdx in json.dealdata[dealIdx].associations["line items"].results) {
let lineItemsData = json.dealdata[dealIdx].associations["line items"].results[resultIdx].lineitemsdata
if (lineItemsData != undefined && lineItemsData.length === 0 ) {
delete json.dealdata[dealIdx].associations["line items"].results[resultIdx].lineitemsdata
}
}
}

I would like to create a set of constants based on the result of a .map

I am currently iterating over a .map JSON result from an API, and creating constants for each individual result I'd like to use. I'd like to be able to automate this, as in for when the map detects the "name" key, it creates a constant called "index0Name", followed by the value of "name", or the "_text" key and its value.
Is this possible? Example returned JSON API info below
{
"_attributes": {
"key": "10811142-ce47-44ea-8bdc-5e9f1391f179",
"number": "5",
"type": "GT",
"title": "Title 1- Element Red.gtzip",
"shortTitle": "Title 1- Element Red.gtzip",
"state": "Paused",
"position": "0",
"duration": "0",
"loop": "False",
"selectedIndex": "0"
},
"_text": "Title 1- Element Red.gtzip",
"text": [
{
"_attributes": {
"index": "0",
"name": "Headline.Text"
},
"_text": "HEADLINE"
},
{
"_attributes": {
"index": "1",
"name": "Description.Text"
},
"_text": "Description"
}
],
"image": {
"_attributes": {
"index": "0",
"name": "Logo Image.Source"
},
"_text": "4b42ada2-8745-4744-8104-fa42c18b3d6b\\IMAGE1.gif"
}
}
Current code is:
const modifiedMap = mapResult[0];
let inputMap = [];
if (modifiedMap) {
inputMap = modifiedMap.map((input, i) => {
const title = input._attributes.title;
const state = input._attributes.state;
const number = input._attributes.number;
const type = input._attributes.type;
// React result in here
}
return inputMap
}
In the above code, where it manually sets the constant "title" based on what it finds at input._attributes.title, I would like the map to
Iterate over input._attributes
Create a constant based on what it finds (Title, state, number...)
Set the value of that constant based on the value it finds of the corresponding key
Any help is appreciated as always.

How to convert array object in snowflake

I would like to convert the below array of objects into another form (varname is ignored as its not required, key and value is used to generate the output form). Any leads would be appreciated
Input array:
[
{
"key": "string_U6",
"value": "grandwagoneer",
"varname": "pagenameplate"
},
{
"key": "string_U13",
"value": "2021",
"varname": "year"
}
]
Output
[
{
"string_U6": "grandwagoneer"
},
{
"string_U13": "2021"
}
]
You could try using map as below:
var input = [ { "key": "string_U6", "value": "grandwagoneer", "varname": "pagenameplate" }, { "key": "string_U13", "value": "2021", "varname": "year" } ];
var output = input.map(function(entry){
let obj = {};
obj[entry.key] = entry.value;
return obj;
});
console.log(output);
As the question asked how to convert array object in snowflake, I wanted to share Snowflake way to do it:
-- SQL to create a sample table with data
create table sample_table (v variant )
as select parse_json(' [ { "key": "string_U6", "value": "grandwagoneer", "varname": "pagenameplate" },
{ "key": "string_U13", "value": "2021", "varname": "year" } ]');
-- query to parse the variant and create the array:
select ARRAY_AGG( OBJECT_CONSTRUCT(i.value:key::varchar, i.value:value::varchar) )
from sample_table,
lateral flatten ( sample_table.v ) i;
It will produce exact output you want.

Get the object value based on input objects in javascript

I would like to how to get the value based on input objects in javascript.
If the source matches the currency and also matches the type in paymentin and paymentout in object then get the value of speed and fees
for example 'SGD' with type_in 'credit' and type_out 'bank' should return the speed and fee
Expected Output:
id: transfer credit: 1 days 1% pay_in: pay_out: bank
id: insta debit: 1 days 1.5% pay_in: pay_out: bank
I tried but i got stuck
function getValue(source,typein,typeout,obj){
var filterArr = source.filter(function(value){
return value.country_from.filter(function(payin){
const in= payin.paymentIn.filter(function(ty){
return ty.type == typein
})
const out = payin.paymentIn.filter(function(ty){
return ty.type == typeout
})
})
})
}
var result = getValue(source,type_in,type_out,obj);
//input objects
var source="SGD";
var type_in="credit";
var type_out="bank";
var obj = [{
"id": "transfer",
"country_from": [{
"currency": [
"SGD",
"USD"
],
"paymentIn": [{
"type": "credit",
"speed": {
"unit": "days",
"number": "1"
},
"fee": {
"type": "%",
"number": "1"
}
}],
"paymentOut": [{
"type": "bank",
"speed": {
"unit": "days",
"number": "2"
}
}]
}]
}, {
"id": "insta",
"country_from": [{
"currency": [
"SGD",
"USD"
],
"paymentIn": [{
"type": "credit",
"speed": {
"unit": "days",
"number": "1"
},
"fee": {
"type": "%",
"number": "1.5"
}
}],
"paymentOut": [{
"type": "bank",
"speed": {
"unit": "days",
"number": "2"
}
}]
}]
}]
I think you have made some mistakes on your initial code, but I guess it is due to the confusion of dealing with so many layers of objects and arrays. This is what you should do:
const getValue = (source, typein, typeout, obj) => {
const res = obj.map(item => {
if (item['country_from'][0]['paymentIn'][0]['type'] === typein
&& item['country_from'][0]['currency'].includes(source)
&& item['country_from'][0]['paymentOut'][0]['type'] === typeout) {
return `id: ${item['id']} credit: ${item['country_from'][0]['paymentIn'][0]['speed']['number']} days credit: ${item['country_from'][0]['paymentIn'][0]['fee']['number']}${item['country_from'][0]['paymentIn'][0]['fee']['type']} pay_in: pay_out: ${item['country_from'][0]['paymentOut'][0]['speed']['number']}`
}
});
return res;
}
getValue('SGD', 'credit', 'bank', obj);
Basically, I will iterate through every element of the input array, obj (which is the one you posted on your question), and within each iteration, I check for the following 3 conditions using the if statement.
1) The paymentIn type matches typein
2) The paymentOut type matches typein
3) currency contains source
Elements that fulfil the above 3 conditions will the string result.
EDIT: To answer your question on the comments, if the paymentIn array has more than one object, we can use Array.some() to check if the object with the type property has the same value as typeIn.
if (item['country_from'][0]['paymentIn'].some(payment => payment['type']===typein)
&& item['country_from'][0]['currency'].includes(source)
&& item['country_from'][0]['paymentOut'][0]['type'] === typeout) {
// to get the object with that typein
const filteredPaymentIn = item['country_from'][0]['paymentIn'].filter(payment => payment['type']===typein)[0];
}

Need to make on object in Javascript based on an existing object which is efficient considering its performance

{
"name": "test name",
"description": "test desc",
"data_table_id": 3,
"column_0": {
"value": "1",
"label": "name"
},
"condition_0": {
"value": "101",
"label": "Is equal to"
},
"column_1": {
"value": "2",
"label": "age"
},
"condition_1": {
"value": "102",
"label": "Is less than"
}
}
I have the above object in JavaScript. From this object I need to create the following object. Need to find a way which is good from performance point of view. The below conditions array is based on the object starting with 'column_' in the above object.
For example: if there are column_0, column_1, column_2, the length of conditions array will be 3. These columns will be coming dynamically, can be from 0-n, n = any integer >= 0. (i.e. column_0 - column_n)
The same condition applies for condition_0, condition_1. Also, condition_0 is always associated with column_0, condition_1 is always associated with column_1 ans so on.
{
"name": "test name",
"description": "test desc",
"data_table_id": 3,
"conditions" : [
{
"column_id": 1, // column_0.value
"column_name": "name", // column_0.label
"condition_id": 101 // condition_0.value
},
{
"column_id": 2, // column_1.value
"column_name": "age", // column_1.label
"condition_id": 102 // condition_1.value
}
],
}
extract the conditions using ...rest, reduce the Object.entries , construct the data structure and push it to the resulting array, finally put everything back together :
const data = {
"name": "test name",
"description": "test desc",
"data_table_id": 3,
"column_0": {
"value": "1",
"label": "name"
},
"condition_0": {
"value": "101",
"label": "Is equal to"
},
"column_1": {
"value": "2",
"label": "age"
},
"condition_1": {
"value": "102",
"label": "Is less than"
}
}
const {
name,
description,
data_table_id,
...rest
} = data;
const conditions = Object.entries(rest).reduce((all, [key, obj]) => {
if (key.startsWith('condition')) {
const id = key.split('_')[1];
const condition = {
"column_id": rest[`column_${id}`].value,
"column_name": rest[`column_${id}`].label,
"condition_id": obj.value,
}
all.push(condition)
}
return all;
}, []);
const result = {
name,
description,
data_table_id,
conditions
}
console.log(result)

Categories