How to get Keys and values of a JSON object in React.js

38,645

What you need here is recursion.

Though, there is something missing in your design, you are not defining the exact condition for when should the program actually extract the value.

For example, lets define that if a value of a given key is a string, then we will render it to the page.

Such condition will look something like this:

if (typeof data[key] === 'string' || data[key] instanceof String)

Then with this decision, you can write a function that will take a data object and recursively will go over the object's keys, based on the condition above, it will return the value Or another call to the same function with the value as the new data object.

The function will look something along this block of code:

function generateData(data) {
  const newData = Object.keys(data).reduce((result, currentKey) => {
    if (typeof data[currentKey] === 'string' || data[currentKey] instanceof String) {
      result.push(currentKey);
    } else {
      const nested = generateData(data[currentKey]);
      result.push(...nested);
    }
    return result;
  }, []);
  return newData;
}

I'm using .reduce here to build a new array. and as mentioned above, i will push to the new array either the key or the result of a recursive call to the function with the value as the new data object, of course based on the condition mentioned above.
Note, that the recursive call will return an array, so i'm using the spread syntax to extract it's members.

Now, we want our key and value to get rendered to the DOM.
So instead to just push the key or value, we can push an element.

Here is a full running example:

const myData = {
  "key1": "value1",
  "key2": "value2",
  "key3": "value3",
  "key4": {
    "k1": "val1",
    "k2": {
      "p1": "v1",
      "p2": "v2",
      "p3": "v3"
    }
  }
}

const generateElement = (key,value) => {
  return (
    <div key={key} className="row">
      <div className="col-xs-6 ins-label">{key}</div>
      <div className="col-xs-6">{value}</div>
    </div>
  );
}

function generateData(data) {
  const newData = Object.keys(data).reduce((result, currentKey) => {
    if (typeof data[currentKey] === 'string' || data[currentKey] instanceof String) {
      const elementToPush = generateElement(currentKey, data[currentKey]);
      result.push(elementToPush);
    } else {
      const nested = generateData(data[currentKey]);
      result.push(...nested);
    }
    return result;
  }, []);
  return newData;
}

class App extends React.Component {
  render() {
  const { data } = this.props;
    return (
      <div>
        {generateData(data)}        
      </div>
    )
  }
}

ReactDOM.render(<App data={myData} />, document.getElementById('root'));
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Share:
38,645
user2138675
Author by

user2138675

Updated on October 06, 2020

Comments

  • user2138675
    user2138675 over 3 years

    I am new to react and trying to implement all the key and values of a JSON object in my web page. I have a below JSON structure. I want all the keys and values to be printed in my web page where key should be printed as labels.

    {
        "key1": "value1",
        "key2": "value2",
        "key3": "value3",
        "key4":{
           "k1": "val1",
            "k2": {
            "p1": "v1",
            "p2": "v2",
            "p3": "v3"
                }
              }
            }
    

    I have written the below code to get the keys and values of this JSON.

    getValues() {
            let arr = [];
            arr = Object.keys(this.props.myArr).map(key =>
                <div key={this.props.data[key]} className="row">
                    <div className="col-xs-6">{key}</div>
                    <div className="col-xs-6">{this.props.myArr[key]}
                    </div>
                </div>
    
            )
            return arr;
        }
    render() {
        return (
            <div>
                <Header />
                <div className="content">
                    {this.getValues()}
                </div>
            </div>
        )
    }
    

    }

    props.myArr has the entire object.

    I am able to print the keys and values but not the nested object under key4(p1,p2,p3). I want all the keys along with its values to be printed one after another in my web page. Can someone tell me where am i going wrong and how to access all the keys (as labels) and values of the JSON object.

    Edited-

    const myObj = {
      "key1": "value1",
      "key2": "value2",
      "key3": "value3",
      "key4": {
        "k1": "val1",
        "k2": {
          "p1": "v1",
          "p2": "v2",
          "p3": "v3"
        }
      }
    }
    
    
    class Details extends React.component{
    constructor(){
      this.getValues = this.getValues.bind(this);
    }
    getValues() {
            let arr = [];
            arr = Object.keys(myObj).map(key =>
                <div key={myObj[key]} className="row">
                    <div className="col-xs-6">{key}</div>
                    <div className="col-xs-6">{myObj[key]}
                    </div>
                </div>
    
            )
            return arr;
        }
    
    generateData(myObj) {
    
            const newData = Object.keys(myObj).reduce((result, currentKey) => {
              if (typeof myObj[currentKey] === 'string' || myObj[currentKey] instanceof String) {
                const elementToPush = getValues(currentKey, myObj[currentKey]);
                result.push(elementToPush);
              } else {
                const nested = generateData(myObj[currentKey]);
                result.push(...nested);
              }
              return result;
            }, []);
            return newData;
          }
    
    render() {
        return (
            <div>
                <Header />
                <div className="content">
                    {this.generateData(myObj)}
                </div>
            </div>
        )
    }
    }
    

    }

    P.S I am using getValues with map function to retrieve first level of key and values as the provided syntax by you under generateElement() gives me error.

  • user2138675
    user2138675 over 6 years
    Thanks for providing the solution.Let me try this.So we do not need map function and Object.keys anymore?
  • Sagiv b.g
    Sagiv b.g over 6 years
    I'm using Object.keys in generateData. And i went with .reduce instead of .map.
  • user2138675
    user2138675 over 6 years
    Just curious to know if the above code you provided will work with any level of object nesting data and will print the keys and values regardless of the level of nesting the JSON would have.
  • Sagiv b.g
    Sagiv b.g over 6 years
    Yes, as long as you keep this shape of object. i mean, a key can be either a String or an Object. note that we are not checking for Array in our condition.
  • user2138675
    user2138675 over 6 years
    You are passing data inside generateElement () which should be the variable i assume which has the entire data so I am passing my entire object which is this.props.myArr or myData inside generateElement function but that is giving me error says its not defined.
  • Sagiv b.g
    Sagiv b.g over 6 years
    you need to pass it to generateData, and generateData will call generateElement with the relevant key, value pair.
  • user2138675
    user2138675 over 6 years
    yeah same.typo that was.I am passing 'myData' into generateData() but that gives me error says its not defined. Also, i am not able to use the given syntax for generateElement().It gives me syntax error at =(key,value). Should i use Object.maps?
  • Sagiv b.g
    Sagiv b.g over 6 years
    myData is just a variable i used in the example. make sure you pass a valid defined object. as for the syntax error you get, it maybe due to the reason i'm using arrow functions (which i saw you are using as well by the way). maybe post an update to your question with the new syntax (don't delete the old syntax from the question just add another edit section)
  • user2138675
    user2138675 over 6 years
    I have updated my changes above. The error i get now is getValues() is not defined.
  • Sagiv b.g
    Sagiv b.g over 6 years
    you forgot to call it with this -> this.getValues(), you will also need to bind generateData in the constructor
  • user2138675
    user2138675 over 6 years
    this.getValues = this.getValues.bind(this) is not correct?
  • Sagiv b.g
    Sagiv b.g over 6 years
    when you call it: this.getValues(currentKey, myObj[currentKey])
  • user2138675
    user2138675 over 6 years
    That works but now I see error as Objects are not valid as a React child (found: object with keys {k1, k2}). If you meant to render a collection of children, use an array instead.
  • user2138675
    user2138675 over 6 years
    This is the response i am getting from backend so cant really change the format.
  • Sagiv b.g
    Sagiv b.g over 6 years
    if you want to use my example (which is fully working and running as you can see) that just copy paste the code. you are using half of what i wrote, so obviously you will get errors.
  • user2138675
    user2138675 over 6 years
    I was facing issue because of something else.Finally its working.Thanks a ton :)
  • user2138675
    user2138675 over 6 years
    Help much appreciated.I would mark this as completed now.