jsontypescriptalpha-vantage

Parsing a JSON file to custom object types in typescript


I am calling the AlphaVantage API that gives me this JSON response:

{
    "Meta Data": {
        "1. Information": "Intraday (5min) open, high, low, close prices and volume",
        "2. Symbol": "IBM",
        "3. Last Refreshed": "2024-04-19 19:55:00",
        "4. Interval": "5min",
        "5. Output Size": "Compact",
        "6. Time Zone": "US/Eastern"
    },
    "Time Series (5min)": {
        "2024-04-19 19:55:00": {
            "1. open": "181.5200",
            "2. high": "181.5800",
            "3. low": "181.4600",
            "4. close": "181.5100",
            "5. volume": "158"
        },
        "2024-04-19 19:40:00": {
            "1. open": "181.2000",
            "2. high": "181.2000",
            "3. low": "181.2000",
            "4. close": "181.2000",
            "5. volume": "27"
        },
        "2024-04-19 19:30:00": {
            "1. open": "181.5600",
            "2. high": "181.5600",
            "3. low": "181.5600",
            "4. close": "181.5600",
            "5. volume": "20"
        },
        "2024-04-19 19:25:00": {
            "1. open": "181.5600",
            "2. high": "181.5600",
            "3. low": "181.5600",
            "4. close": "181.5600",
            "5. volume": "1"
        },...
    }
}

I am trying to extract the data and store it in a object using a interface:

type IntradayInterface = {
    metaData:          MetaData;
    interval: { [time: string]: IntervalData };
}

export type MetaData = {
    information:    string;
    symbol:         string;
    lastRefreshed:  Date;
    interval:       string;
    outputSize:     string;
    timeZone:       string;
}

export type IntervalData = {
    open:   string;
    high:   string;
    low:    string;
    close:  string;
    volume: string;
}

export default IntradayInterface;

And here's my parser function:

function parseIntradayData(jsonData: any) : IntradayInterface {
    
    const metaData = jsonData['Meta Data'];

    let parsedMetaData : MetaData = {
        information: metaData['1. Information'],
        symbol: metaData['2. Symbol'],
        lastRefreshed: metaData['3. Last Refreshed'],
        interval: metaData['4. Interval'],
        outputSize: metaData['5. Output Size'],
        timeZone: metaData['6. Time Zone']
    };

    const intervalData = jsonData['Time Series (5min)'];

    let parsedIntervalData: IntervalData = {
        open: intervalData['1. open'],
        high: intervalData['2. high'],
        low: intervalData['3. low'],    
        close: intervalData['4. close'],
        volume: intervalData['5. volume']
    }

    const returnData: IntradayInterface = {
        metaData: parsedMetaData,
        interval: intervalData
    } 

    return returnData;
}
export default parseIntradayData;

The goal is to extract the data from the JSON response and store it in an object of type IntradayInterface but the object is not populated correctly when I try. I have two assumptions:

1 - My Interface isn't setup correctly, specifically the interval definition. I want to be able to also store each datetime that maps to the IntervalData but I don't know how

2 - I am not parsing the interval "(Time Series 5min)" part of the JSON correctly

Any help with this would be greatly appreciated!


Solution

  • The "Time Series (5min)" object in your input data has some nested objects, which you also want to transform. So you have to iterate over all of them

    function parseIntradayData(jsonData: any) : IntradayInterface {
        
        const metaData = jsonData['Meta Data'];
        const parsedMetaData : MetaData = {
            information: metaData['1. Information'],
            symbol: metaData['2. Symbol'],
            lastRefreshed: metaData['3. Last Refreshed'],
            interval: metaData['4. Interval'],
            outputSize: metaData['5. Output Size'],
            timeZone: metaData['6. Time Zone']
        };
    
        const intervalData: Record<string, object> = jsonData['Time Series (5min)'];  //hint a type here, so
        const parsedIntervalData = Object.fromEntries(Object.entries<any>(intervalData).map(([k,v]) => {
          let t = {
            open: v['1. open'],
            high: v['2. high'],
            low: v['3. low'],    
            close: v['4. close'],
            volume: v['5. volume']
          };
          return [k, t];
        }));
    
        const returnData: IntradayInterface = {
            metaData: parsedMetaData,
            interval: parsedIntervalData
        } 
    
        return returnData;
    }
    

    Object.entries transforms an object into an array of [key,value] pairs. Then you can iterate over this array to transform your values. Finaly, Object.fromEntries transforms the resulting [key,value] pairs back to an object.

    And looking at the mapping of the IntervalData you can probably make that even simpler

    let t = Object.fromEntries(Object.entries(v).map(([kk, vv]) => [kk, vv.split(" ")[1]])) as IntervalData;
    

    because the mapping of the property names is just done by removing the 1. , 2. ... from the property name. But as Object.fromEntries does not infer any typ

    Something similar could probably also be done with the MetaData object.