Home > Mobile >  Checking if a JSON property exists
Checking if a JSON property exists

Time:05-17

I am importing stock JSON data from Yahoo Finance to import into Google Sheets for a personal stock tracker using a custom function:

function _yahoofinance(ticker) {
  const url = 'https://query2.finance.yahoo.com/v10/finance/quoteSummary/'   encodeURI(ticker)
              '?modules=price,assetProfile,summaryDetail,incomeStatementHistory,'
              'balanceSheetHistory,defaultKeyStatistics,financialData,calendarEvents,'
              'recommendationTrend,upgradeDowngradeHistory,majorHoldersBreakdown'
  ;

  const response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
  const responseCode = response.getResponseCode();
  
  if (responseCode === 200) {
    const quote = JSON.parse(response.getContentText());

    const price       = _isEmpty(quote.quoteSummary.result[0].price.regularMarketPrice)     ? '-' : quote.quoteSummary.result[0].price.regularMarketPrice.fmt; 
    const divYield    = _isEmpty(quote.quoteSummary.result[0].summaryDetail.dividendYield)  ? '-' : quote.quoteSummary.result[0].summaryDetail.dividendYield.raw * 100;
    const payoutRatio = _isEmpty(quote.quoteSummary.result[0].summaryDetail.payoutRatio)    ? '-' : quote.quoteSummary.result[0].summaryDetail.payoutRatio.raw * 100;
    const marketCap   = _isEmpty(quote.quoteSummary.result[0].summaryDetail.marketCap)      ? '-' : quote.quoteSummary.result[0].summaryDetail.marketCap.fmt;
    const fwdPE       = _isEmpty(quote.quoteSummary.result[0].summaryDetail.forwardPE)      ? '-' : quote.quoteSummary.result[0].summaryDetail.forwardPE.fmt;
    const revenue     = _isEmpty(quote.quoteSummary.result[0].financialData.totalRevenue)   ? '-' : quote.quoteSummary.result[0].financialData.totalRevenue.fmt;
    const revGrowth   = _isEmpty(quote.quoteSummary.result[0].financialData.revenueGrowth)  ? '-' : quote.quoteSummary.result[0].financialData.revenueGrowth.raw * 100;
    const earnGrowth  = _isEmpty(quote.quoteSummary.result[0].financialData.earningsGrowth) ? '-' : quote.quoteSummary.result[0].financialData.earningsGrowth.raw * 100;
    const freeCash    = _isEmpty(quote.quoteSummary.result[0].financialData.freeCashflow)   ? '-' : quote.quoteSummary.result[0].financialData.freeCashflow.fmt;


      return [[divYield, payoutRatio, marketCap, fwdPE, revenue, revGrowth, earnGrowth, freeCash]];
  }
}

function _isEmpty(obj) {
    return Object.keys(obj).length === undefined;
}

Please see an example sheet here (tab JSON).

The problem is: not every ticker will have every property that I'm requesting from Yahoo. I have tried in many different ways to deal with this:

const payoutRatio = _isEmpty(quote.quoteSummary.result[0].summaryDetail.payoutRatio)

function _isEmpty(obj) {
    return Object.keys(obj).length === undefined;
}

But none of this works. In the example sheet I am trying to pull data on ticker 0P0001703K.SW, which is an ETF, so it will not have all the properties that stocks have. Google Sheets returns an error:

#Error! TypeError: Cannot read property 'totalRevenue' of undefined (line 19).

It is driving me half insane :D I have been trying to catch such errors in so many ways. My question is: what is the proper way to check if a property exists, and if not, just return - and move on to the next property? Your help would be greatly appreciated!

[EDIT] Solution:

function _yahoofinance(ticker) {
  const url = 'https://query2.finance.yahoo.com/v10/finance/quoteSummary/'   encodeURI(ticker)
              '?modules=price,assetProfile,summaryDetail,incomeStatementHistory,'
              'balanceSheetHistory,defaultKeyStatistics,financialData,calendarEvents,'
              'recommendationTrend,upgradeDowngradeHistory,majorHoldersBreakdown'
  ;

  const response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
  const responseCode = response.getResponseCode();
  
  if (responseCode === 200) {
    const quote = JSON.parse(response.getContentText());

    const price       = quote.quoteSummary.result[0]?.price?.regularMarketPrice?.fmt     || '-'; 
    const divYield    = quote.quoteSummary.result[0]?.summaryDetail?.dividendYield?.raw  ?  '-' : quote.quoteSummary.result[0].summaryDetail.dividendYield.raw * 100;
    const payoutRatio = quote.quoteSummary.result[0]?.summaryDetail?.payoutRatio?.raw    ?  '-' : quote.quoteSummary.result[0].summaryDetail.payoutRatio.raw * 100;
    const revenue     = quote.quoteSummary.result[0]?.financialData?.totalRevenue?.fmt   || '-';

    return [[price, divYield, payoutRatio, revenue]];
  }
}

CodePudding user response:

You can use optional chaining instead of your _isEmpty() function because you check if quote.quoteSummary.result[0].financialData.totalRevenue is empty but error says 'totalRevenue' of undefined. Thats means that financialData is undefined and it throws error even before it goes to your isEmpty function.

In your example it would be something like:

const revenue = quote.quoteSummary.result?.[0]?.financialData?.totalRevenue?.fmt || '-';

Prove it checks undefined (after last comment):

const quote = {
  quoteSummary: {
    result: [{}]
  }
}
const revenue = quote.quoteSummary.result?.[0]?.financialData?.totalRevenue?.fmt || '-';
console.log(revenue);

  • Related