React iterate through object nested in array

I’m working with some JSON data in a React app using Axios and I am having trouble iterating through an object nested in an array. I think some variation of .map or Object.keys would work but I am stuck.

I’ve made a Stack Overflow post

Any ideas on how to fix this?

for (const result of results) console.log(result)

or

`for (const {data, links, href} of results) …

or

results.forEach(({data, links, href) =>

etc.

I assume you don’t just want to log though, map is normally what you’d use. You need to provide a bit more context, because you have the data and it’s easy to iterate over, so assuming there may be another issue?

I want to access the data in the object and then create a div that displays the data.

Maybe something like

class ResultsDisplay extends React.Component {
  render() {
    return (
      <React.Fragment>
      {this.props.results.map(({data, links, href}) => <Result data={data} links={links} href={href} />)}
      </React.Fragment>
    )
  }
}

And then Result would be a div, and in that you render out data/links/href however. Or you use a <ul> rather than a fragment and each Result is a <li> or whatever

import React, { Component } from 'react';
import Autosuggest from 'react-autosuggest';
import axios from 'axios';
import {list} from './SearchSuggestions';

// Importing list const that contains search terms to suggest

// Teach Autosuggest how to calculate suggestions for any given input value.
const getSuggestions = value => {
  const inputValue = value.trim().toLowerCase();
  const inputLength = inputValue.length;

  return inputLength === 0 ? [] : list.filter(searchTerm =>
    searchTerm.name.toLowerCase().slice(0, inputLength) === inputValue
  );
};

// When suggestion is clicked, Autosuggest needs to populate the input
// based on the clicked suggestion. Teach Autosuggest how to calculate the
// input value for every given suggestion.
const getSuggestionValue = suggestion => suggestion.name;

// Use your imagination to render suggestions.
const renderSuggestion = suggestion => (
  <div>
    {suggestion.name}
  </div>
);

class SearchBar extends Component {
  constructor() {
    super();

    // Autosuggest is a controlled component.
    // This means that you need to provide an input value
    // and an onChange handler that updates this value (see below).
    // Suggestions also need to be provided to the Autosuggest,
    // and they are initially empty because the Autosuggest is closed.
    this.state = {
      value: '',
      suggestions: [],
      results: []
    };
  }

  onChange = (event, { newValue }) => {
    this.setState({
      value: newValue
    });
  };

  // Autosuggest will call this function every time you need to update suggestions.
  // You already implemented this logic above, so just use it.
  onSuggestionsFetchRequested = ({ value }) => {
    this.setState({
      suggestions: getSuggestions(value)
    });
  };

  // Autosuggest will call this function every time you need to clear suggestions.
  onSuggestionsClearRequested = () => {
    this.setState({
      suggestions: []
    });
  };


  loadSearchResults = (event) => {
    event.preventDefault();
    var search = this.state.value;

    axios.get(`https://images-api.nasa.gov/search?q=` + `search`)
      .then(res => res.data.collection.items)
        .then(json => {
          json.map(obj => this.setState({ results: Object.values(obj) }))
    })
  }

  render() {
    const { value, suggestions } = this.state;

    // Autosuggest will pass through all these props to the input.
    const inputProps = {
      placeholder: 'Search for space related topic...',
      value,
      onChange: this.onChange
    };


    const searchResults = Object.keys(this.state.results).map(key =>
      <item key={key} value={key}>{this.state.results[key]}</item>
    )


  console.log(this.state.results);

    // Finally, render it!
    return (
      <div>
      <Autosuggest
        suggestions={suggestions}
        onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
        onSuggestionsClearRequested={this.onSuggestionsClearRequested}
        getSuggestionValue={getSuggestionValue}
        renderSuggestion={renderSuggestion}
        inputProps={inputProps}
      />

      <button onClick={this.loadSearchResults.bind(this)}>Search</button>

      <div>{searchResults}</div>
      </div>
    );
  }
}

export default SearchBar;

Still can’t get this working. This code is resulting in an error: Objects are not valid as a React child (found: object with keys {href, rel, render}). If you meant to render a collection of children, use an array instead.

Can anyone explain the common approach/design pattern that applies to displaying Object data in React? There doesn’t seem to be any method to the madness. I’ve had people on StackOverflow say use Object.keys, Object.value, Arr.map, etc… and nothing works in my program.

I don’t think this has anything to do with React, rather a misunderstanding re iterating over things in JavaScript. Can you explain what you expect to get here?

Honestly I’m not even sure. The two then() sections are from a response I got on StackOverflow. I just want to be able to grab the data in the object nested within the array and display it in html on the page.

  • is the query string correct? You’re literally searching for https://images-api.nasa.gov/search?q=search every time.
  • res.data.collection.items: that is already an array of items. What do you expect to be in that array that means you need to map over it then generate an array for every value?

I think you’re copy pasting answers without thinking; people are trying to answer a half-formed question here

No it doesn’t search for q=search, it takes what the user input in the searchbar and passes that string to the query.

i.e. if the user types “mars” it sends an API request to https://images-api.nasa.gov/search?q=mars

If you look at how that JSON is structured it has multiple layers of objects nested inside arrays. I am able to access the array data but not the object data. For example check out this part of the JSON:

items": [
      {
        "links": [
          {
            "rel": "captions",
            "href": "https://images-assets.nasa.gov/video/50 Years of Mars Exploration /50 Years of Mars Exploration .srt"
          }
        ],
        "href": "https://images-assets.nasa.gov/video/50 Years of Mars Exploration /collection.json",
        "data": [
          {
            "nasa_id": "50 Years of Mars Exploration ",
            "title": "50 Years of Mars Exploration ",
            "media_type": "video",
            "date_created": "2015-08-20T00:00:00Z",
            "center": "HQ",
            "keywords": [
              "Mars"
            ],
            "description": "2015 marks 50 years of successful NASA missions to Mars starting with Mariner 4 in 1965. Since then, a total of 15 robotic missions led by various NASA centers have laid the groundwork for future human missions to the Red Planet. The journey to Mars continues with additional robotic missions planned for 2016 and 2020, and human missions in the 2030s."
          }
        ]
      },

I can access the items array but not the objects inside it. How would I, for example, pull the data from the description key/value pair?

“description”: “2015 marks 50 years of successful NASA missions to Mars starting with Mariner 4 in 1965. Since then, a total of 15 robotic missions led by various NASA centers have laid the groundwork for future human missions to the Red Planet. The journey to Mars continues with additional robotic missions planned for 2016 and 2020, and human missions in the 2030s.”

items[0].data[0].description in that instance but if you’re mapping over the items then - the items

1 Like

I ended up getting it to work like this. In the render method I set const data = this.state.results;

Then I mapped through it in the return function like this:

 <div className="container">
      {data.map(function(d, index){
         return (
           <div>
           <li key={index}>{d.data[0].title}</li>
           <li key={index}>{d.data[0].description}</li>
           <li key={index}>{d.data[0].date_created}</li>
           <li key={index}>{d.data[0].keywords}</li>
           <br/>
           </div>
         )
       })}
</div>

But I’m still running into the “object within an array” issue when I try to access the images where are located inside the link object within the array.

1 Like

You sure it’s an image? In the example response you’ve shown here, it’s a video and the link field is for subtitle files. If it’s images, what do you expect the data to look like?

Could you explain what ’ d ’ and ’ key={index} ’ does?
your code helped me get my code working but I just don’t understand it.

Although my code compiles, I get this > Warning: Each child in a list should have a unique “key” prop.

d is just the name of a function parameter, it could be called anything at you want. d has been used as the name, as the array (plural) is data and the map iterates over each datum. This isn’t some special React thing, it’s just how JS works (and most programming languages), you can call parameters whatever you want.

If you generate a collection of React elements using map/filter/etc, you should provide a key property to each element to allow React to avoid rendering issues

Thank you for the response. So in other words, data.map(function(d, index),
d represents data. What about key={index}?

See second paragraph and link above