React Nested JSON mapping - HELP

Hello all,

I have been attempting to practice iterating over arrays and objects in React and am having trouble in particular with mapping a nested JSON object into a html table. I always get ‘not iterable error’ or no return at all. Can anyone give me some advice or a starting point for what I am attempting to do (push a JSON object to state via api and map state out to an html table). Any help would be appreciated - been trying to work this out for a few days.

Codepen Here

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      key: "KOS1RXP2ZUGW0XKIKVNYZBK4ZJAZP5KB",
      ticker: "RKT",
      strikeCount: "5",
      strategy: "ANALYTICAL",
      startDate: "2021-08-30",
      endDate: "2021-09-30",
      data: []
      //count: 1
    };
  }

  updateTable = () => {
    const request = `https://api.tdameritrade.com/v1/marketdata/chains?apikey=${this.state.key}&symbol=${this.state.ticker}&strikeCount=${this.state.strikeCount}&strategy=${this.state.strategy}&fromDate=${this.state.startDate}&toDate=${this.state.endDate}`;

    fetch(request)
      .then((response) => response.json())
      .then((data) => display(data));

    let display = (data) => {
      this.setState({
        data: [{ ...data.callExpDateMap }]
      });
    };
  };

  componentDidMount() {
    this.updateTable();
  }

  //  componentDidMount() {
  //  this.intervalId = setInterval(this.updateTable, 1000);
  //}

  //componentWillUnmount() {
  //  clearInterval(this.intervalId);
  //}

  render() {
    console.log(this.state.data);
    return (
      <div>
        <table>
          <tr>
            <th>Date</th> {/*Need multiple dates into separate columns}*/}
            <th>Bid</th>
            <th>Ask</th>
          </tr>
          <tr>
            {" "}
            {/*Need new row for each data point*/}
            <td>Strike</td>
            <td>Bid</td>
            <td>Ask</td>
          </tr>
        </table>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("data"));

First of all, in JS there is no such thing as a “JSON object”. JSON is a format for objects that are a subset of JS objects. In JS, JSON is either a string or is is converted to a JS object. You cannot map over JSON, until it’s been converted to a JS object.

What do you want a data point to be? I see a complex object here:

[
  {
    2021-09-03:1: {
      16.5: [
        {
          putCall: "CALL",
          symbol: "RKT_090321C16.5",
          description: "RKT Sep 3 2021 16.5 Call (Weekly)",
          exchangeName: "OPR",
          bid: 0.8,
          ask: 0.87,
          last: 0.93,
          mark: 0.84,
          bidSize: 13,
          askSize: 35,
          bidAskSize: "13X35",
          lastSize: 0,
          highPrice: 0.93,
          lowPrice: 0.76,
          openPrice: 0,
          closePrice: 0.91,
          totalVolume: 5,
          tradeDate: null,
          tradeTimeInLong: 1630598927561,
          quoteTimeInLong: 1630612799970,
          netChange: 0.02,
          volatility: 45.937,
          delta: 0.948,
          gamma: 0.226,
          theta: -0.013,
          vega: 0.001,
          rho: 0,
          openInterest: 295,
          timeValue: 0.12,
          theoreticalOptionValue: 0.816,
          theoreticalVolatility: 29,
          optionDeliverablesList: null,
          strikePrice: 16.5,
          expirationDate: 1630699200000,
          daysToExpiration: 1,
          expirationType: "S",
          lastTradingDay: 1630713600000,
          multiplier: 100,
          settlementType: " ",
          deliverableNote: "",
          isIndexOption: null,
          percentChange: 1.74,
          markChange: -0.08,
          markPercentChange: -8.65,
          nonStandard: false,
          inTheMoney: true,
          mini: false
        }
      ],
      17.0: [
        {
          putCall: "CALL",
          symbol: "RKT_090321C17",
          description: "RKT Sep 3 2021 17 Call (Weekly)",
          exchangeName: "OPR",
          bid: 0.34,
          ask: 0.4,
          last: 0.35,
          mark: 0.37,
          bidSize: 5,
          askSize: 33,
          bidAskSize: "5X33",
          lastSize: 0,
          highPrice: 0.44,
          lowPrice: 0.33,
          openPrice: 0,
          closePrice: 0.44,
          totalVolume: 663,
          tradeDate: null,
          tradeTimeInLong: 1630612299419,
          quoteTimeInLong: 1630612794602,
          netChange: -0.09,
          volatility: 28.878,
          delta: 0.827,
          gamma: 0.856,
          theta: -0.02,
          vega: 0.003,
          rho: 0,
          openInterest: 1010,
          timeValue: 0.04,
          theoreticalOptionValue: 0.333,
          theoreticalVolatility: 29,
          optionDeliverablesList: null,
          strikePrice: 17,
          expirationDate: 1630699200000,
          daysToExpiration: 1,
          expirationType: "S",
          lastTradingDay: 1630713600000,
          multiplier: 100,
          settlementType: " ",
          deliverableNote: "",
          isIndexOption: null,
          percentChange: -21.17,
          markChange: -0.07,
          markPercentChange: -16.67,
          nonStandard: false,
          inTheMoney: true,
          mini: false
        }
      ],
  //...
]

If I were confronted with this, I might consider transforming your object. What would be the most useful shape for your object/array? I might consider something like:

    fetch(request)
      .then((response) => response.json())
      .then(transformData)
      .then((data) => display(data));

where transformData is a function that takes in the original data and returns it in the shape you want. You could flatten that around, makes some of those object properties (like the date) key/value pairs. You could end up with a simple array of objects (if that fits your needs) that will be easy to map through. I’d rather pull that logic out into its own function.

Thank you for clarifying some of those concepts.

My intent was to bring in the JSON and convert it to an object that I can iterate over and filter based on input from a user. Should I be doing that where I fetch the data?

{Expiration Date: ‘08-25-2021:4’, Strike: ‘16’, Properties: [bid: 0.8, ask: 0.9, etc.]}

I’m trying to spit this out in a table where the columns are the dates. The rows include the strike price and the strike properties under the span of the date column.

Thank you for clarifying some of those concepts.

Hey, I’ve heard plenty of junior devs and I think at least one mid-level dev refer to a JS object as “JSON data”. So, you’re not alone. Sometimes the way they are taught is a little confusing, making it seem like they are the same thing. It makes it a little confusing too because you can essentially use JSON notation for a JS object. But inside JS, it’s just JS.

Yes, that is what this does:

.then((response) => response.json())

what gets returned from that is a JS object. After that part of the promise chain, you are not dealing with JSON, but with a JS object.

…that I can iterate over and filter based on input from a user. Should I be doing that where I fetch the data?

That’s where I would do it. I don’t want to do it in the main body of the component because then it would be running on every render (unless I memoized it). So, yeah, that is a very common pattern that I use. I would probably extract it into a different file or even a custom hook, but for now, this could work. My instinct would be to do what I suggest - write a function that takes in the object that is returned and then processes and returns the shape of object that you want. Putting that in its own function makes it a lot cleaner.

That’s why I suggested this:

    fetch(request)
      .then((response) => response.json())
      .then(transformData)
      .then((data) => display(data));

just adding in a step where you transform your data into whatever you need - changing the shape and getting rid of anything you don’t need. If needed, you could even combine data into new properties.

I don’t know if it throws you, but this:

.then(transformData)

and this:

.then(data => transformData(data))

Similarly, your last line in that chain could almost certainly be written as:

.then(display);

If you’re just using the parameters that are being passed, in the same order, then JS is smart enough to figure it out.

I’m trying to spit this out in a table where the columns are the dates. The rows include the strike price and the strike properties under the span of the date column.

If I’m in this situation (and it has happened) where I know there is a better shape for my data, I will sometimes create dummy data to mess with. I will create what I think the data shape should be and see how that works with my UI (the display portion of the app). Sometimes that helps to clarify what the data shape needs to be. Once I figure that out, then I can write my transforming function.

Thanks a lot for all the advice.

I will try what you suggested and create some dummy data and hopefully derive a function for transformation suitable for what I’m attempting to do.

I’ll also ensure its done outside of the rendering piece of my code.

I really appreciate all the feedback. I feel like I have a fresh place to try to tackle this from again.

1 Like

…hopefully derive a function for transformation suitable for what I’m attempting to do.

I wasn’t trying to trivialize that - it may take a little work. But I’d rather have that messiness hidden away in a helper function than in the meat of my component.

Another advantage of thinking this way is that you can break your code into different sections. I’d much rather work on 20 small problems than 3 huge ones.

To be honest, I probably just create my helper function and have it return the dummy data. But it probably doesn’t matter.

I feel like I have a fresh place to try to tackle this from again.

Cool. Check back if you run in to trouble. Trust me, we all remember what it was like to be where you are now. Just keep at it, you’ll get there.

I have read a bit about flattening JSON and attempted employing this method to simplify my object so I can iterate over it a bit more easily.

Do you think it’d be wise to go this route for what I am attempting to do?

Without flattening, I have been able to pull out each date and each strike for the date, but have been unable to grab the array from each individual strike.

What I didn’t do though was attempt to transform the results into another object like you suggested. I was just trying to display what was returned.

It’s up to me, I wouldn’t hesitate. The shape of your original data is so far away from what you want. You have data in an object, nested in another object, in an array. Yeah, I would want to do it, to make it easy to loop through.

Sure, you could have maps three level’s deep: map the outer array, then “map” the objects in that, and then “map” the objects in those - but that would have to be done on every render. (I don’t think we should be obsessed with that, but it’s good to be aware of it). That very logic we’d be doing there is basically the same as I’m suggesting for the transformer.

But by all means, chose whatever is most comfortable.

So … I’m getting closer I think. Wondering if someone could take a look at what I’ve done so far and help me think this through / steer me somewhere else if I’ve gone about this the wrong way.

I’ve now separated the data into a couple arrays that I can work with and am attempting to build the table out of it.

The problem I’m running into now is getting the same strike data under each date.

If I can get this to work, my other portion would be to build out other columns derived from the strike points object info.

My CodePen

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      key: "KOS1RXP2ZUGW0XKIKVNYZBK4ZJAZP5KB",
      ticker: "RKT",
      strikeCount: "3",
      strategy: "ANALYTICAL",
      startDate: "2021-08-30",
      endDate: "2021-09-30",
      dates: [],
      strikes: []
      //count: 1
    };
  }

  updateTable = () => {
    const request = `https://api.tdameritrade.com/v1/marketdata/chains?apikey=${this.state.key}&symbol=${this.state.ticker}&strikeCount=${this.state.strikeCount}&strategy=${this.state.strategy}&fromDate=${this.state.startDate}&toDate=${this.state.endDate}`;

    let dates = [];
    let strikes = [];

    fetch(request)
      .then(function (response) {
        return response.json();
      })
      .then(function (data) {
        for (let date in Object.getOwnPropertyNames(data.callExpDateMap)) {
          dates.push(Object.getOwnPropertyNames(data.callExpDateMap)[date]);
        }
        for (let key in data.callExpDateMap) {
          let strike = data.callExpDateMap[key];
          strikes.push(Object.keys(strike));
        }
      })
      .then(
        function (data) {
          this.setState({
            dates: dates,
            strikes: strikes
          });
        }.bind(this)
      );
  };

  componentDidMount() {
    this.updateTable();
  }

  //  componentDidMount() {
  //  this.intervalId = setInterval(this.updateTable, 1000);
  //}

  //componentWillUnmount() {
  //  clearInterval(this.intervalId);
  //}

  tableData() {
    let strikes = this.state.strikes;
    console.log(strikes);
    return strikes.map((key, index) => {
      return (
        <tr key={index}>
          <td>{key[index]}</td>
        </tr>
      );
    });
  }

  renderTable() {
    let dates = this.state.dates;
    return dates.map((key, index) => {
      return (
        <div>
          <th className="border p-2">{key.toUpperCase()}</th>
          {this.tableData()}
        </div>
      );
    });
  }

  render() {
    return <div className="p-5">{this.renderTable()}</div>;
  }
}

ReactDOM.render(<App />, document.getElementById("data"));

I don’t understand. Are you saying that you want to get the same strike data under each date and you don’t want to? Or are you saying that you aren’t getting it and want to?

I don’t really know the end result that you want, but what you’re doing seems to make sense. A couple of thoughts:

    fetch(request)
      .then(function (response) {
        return response.json();
      })
      .then(function (data) {
        for (let date in Object.getOwnPropertyNames(data.callExpDateMap)) {
          dates.push(Object.getOwnPropertyNames(data.callExpDateMap)[date]);
        }
        for (let key in data.callExpDateMap) {
          let strike = data.callExpDateMap[key];
          strikes.push(Object.keys(strike));
        }
      })
      .then(
        function (data) {
          this.setState({
            dates: dates,
            strikes: strikes
          });
        }.bind(this)
      );

If you use arrow functions, you don’t need to bind this.

And here:

      .then(function (data) {
        for (let date in Object.getOwnPropertyNames(data.callExpDateMap)) {
          dates.push(Object.getOwnPropertyNames(data.callExpDateMap)[date]);
        }
        for (let key in data.callExpDateMap) {
          let strike = data.callExpDateMap[key];
          strikes.push(Object.keys(strike));
        }
      })

First of all, those both look like good places for a map function, but that’s not necessary if you aren’t comfortable with those.

Secondly, if you return the data you want, it will be passed to the next function in the chain. For example, something like:

      .then(function (data) {
        const dates = [];
        const strikes = [];
        for (let date in Object.getOwnPropertyNames(data.callExpDateMap)) {
          dates.push(Object.getOwnPropertyNames(data.callExpDateMap)[date]);
        }
        for (let key in data.callExpDateMap) {
          let strike = data.callExpDateMap[key];
          strikes.push(Object.keys(strike));
        }
        return { dates, strikes };
      })
      .then(newState => {
          this.setState(newState);
      });

Or something like that.

1 Like

Hi Kevin,

Thanks so much for taking a look at my code. Really taking your advice to heart. I do need to get more familiar with map functions and probably be more consistent with my code like where I use arrow functions.

I was able to accomplish what I’ve been going for with the following - (next state of business will be traversing the object in the strike arrays to fill the rest of the chart):

I was also doing some other research, should I be using something like react-table if I eventually want to be able to filter within the rendered table?

Anyway thank you for the advice.

CodePen Here

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      key: "KOS1RXP2ZUGW0XKIKVNYZBK4ZJAZP5KB",
      ticker: "RKT",
      strikeCount: "5",
      strategy: "ANALYTICAL",
      startDate: "2021-08-30",
      endDate: "2021-09-30",
      dates: [],
      strikes: []
      //count: 1
    };
  }

  updateTable = () => {
    const request = `https://api.tdameritrade.com/v1/marketdata/chains?apikey=${this.state.key}&symbol=${this.state.ticker}&strikeCount=${this.state.strikeCount}&strategy=${this.state.strategy}&fromDate=${this.state.startDate}&toDate=${this.state.endDate}`;

    let dates = [];
    let strikes = [];

    fetch(request)
      .then(function (response) {
        return response.json();
      })
      .then(function (data) {
        for (let date in Object.getOwnPropertyNames(data.callExpDateMap)) {
          dates.push(Object.getOwnPropertyNames(data.callExpDateMap)[date]);
        }
        for (let key in data.callExpDateMap) {
          let strike = data.callExpDateMap[key];
          strikes.push(Object.keys(strike));
        }
      })
      .then(
        function (data) {
          this.setState({
            dates: dates,
            strikes: strikes
          });
        }.bind(this)
      );
  };

  componentDidMount() {
    this.updateTable();
  }

  //  componentDidMount() {
  //  this.intervalId = setInterval(this.updateTable, 1000);
  //}

  //componentWillUnmount() {
  //  clearInterval(this.intervalId);
  //}

  table() {
    let dates = this.state.dates;
    let strikes = this.state.strikes;
    console.log(dates);
    console.log(strikes);

    return dates.map((key, index) => {
      return (
        <div className="border p-2 m-2 shadow-sm rounded">
          <table>
            <th className="border p-2">{key.toUpperCase()}</th>
            <th className="border p-2">Bid</th>
            <th className="border p-2">Ask</th>
            {strikes[index].map((strike, index) => {
              return (
                <tr className="border p-2">
                  <td className="border p-2">{strike}</td>
                  <td className="border p-2"></td>
                </tr>
              );
            })}
          </table>
        </div>
      );
    });
  }

  render() {
    return <div className="p-5">{this.table()}</div>;
  }
}

ReactDOM.render(<App />, document.getElementById("data"));

OK, it looks like you’re on the way.

I was also doing some other research, should I be using something like react-table if I eventually want to be able to filter within the rendered table?

That depends. Personally, if I was learning, I might want to build those things myself, once I got basic functionality down. Or maybe you don’t want to worry about that now and just want to learn how to integrate a cool library. Those are both valid goals.

My advice would be to think like agile/mvp. Think of what the simplest acceptable version of the app is and implement that. Then figure out an improvement and add that. Keep adding things incrementally - don’t try to make it the perfect app all in one go.

Just in terms of React, I might suggest that what you have in for rendering your strikes should be its own component. I would probably have a component Strikes for that and a component Strike that renders each one. I’m such a nerd, I might even have a component for the title bar (which would probably need to be wrapped in a Fragment.)

Another thing to consider … Codepen is a great tool for working on the basics, but it has its limitations. Once you start getting into React, that point of diminishing returns approaches quickly. One big problem is that you can’t break apps into separate files. There are other online IDEs, but I think at some point you’re going to have to learn to code in a more “normal” environment, more like what you would encounter in a work environment. This means using something like create-react-app to create a basic app locally and using an editor like VS Code to edit it. It isn’t as sharable as Codepen, making it more difficult to get help, but you can still push them up to github to share - also a good skill to learn. Maybe it’s not required at this moment, but at some point you may want to consider doing a tutorial or two on these topics and make the transition.

Thank you, I really appreciate your advice. I do plan on slowly adding to it and I think I won’t use a library for now so I can learn.

Today I was able to access the object values and properties of the strikes and splay them out on the table which was pretty exciting.

I did start a VS Code project using what I have so far and realized I have a lot to learn about using a more standard IDE and utilizing Git and GitHub for version control.

Ultimately, I’m trying to change careers and work as a developer, but I feel like I have a long way to go. I figured I needed to start working on one of my own projects outside of the FreeCodeCamp projects.

Again, really appreciate the time you’ve taken.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.