How to output content of a nested object using map in React?

Viewed 141

I have a json that looks like below

const assessmentData = [
    {
        "Sit1": [
            {
                "rule": "Rule1",
                "type": "High"
            }
        ]
    },
    
    {
        "Sit2": [
            {
                "rule": "Rule6",
                "type": "Low"
            }
        ]
    },
    
    {
        "Sit3": [
            {
                "rule": "Rule3",
                "type": "High"
            }
        ]
    }
]

Now I want to render some html that contains the above info. Usually in vanilla HTML, this is what I do

let content = ""

for(let i=0; i < assessmentData.length; i++) {
    for (const [key, value] of Object.entries(assessmentData[i])) {
        content += `<h2>${key}<h2>`
        for (const [subkey, subvalue] of Object.entries(value)) {
            const rule = subvalue["rule"]
            content += `<h3>${rule}</h3>`
        }
    }   
}

So the final output looks like

<h2>Sit1<h2><h3>Rule1</h3><h2>Sit2<h2><h3>Rule1</h3><h2>Sit3<h2><h3>Rule1</h3>

But I can't do the same thing using map functionality. So my code in react looks like

const CreateTemplate = (assessmentData) => {

     const content = assessmentData.map((item, idx) => {
        Object.keys(item).map((subitem, subindex) => {
            <h2>{subitem}</h2>
            Object.keys(item[subitem]).map((subitem2, subindex2) => {
                <h3>{item[subitem][subitem2]["rule"]}</h3>
            })
            
        })
               
     
      });


     return (
        <div>Content</div>
        {content}
     )
}

export default CreateTemplate

It doesn't output the content part. What am I doing wrong?

2 Answers

You should return the values from the map callback. * You can also use Object.entries to map an array of the key-value pairs. Since the value is already an array you don't need to use the keys, A.K.A. the array indices, you can simply map the array values.

const content = assessmentData.map((item, idx) => {
  return Object.entries(item).map(([key, value], subindex) => {
    return (
      <React.Fragment key={subindex}>
        <h2>{key}</h2>
        {value.map((subitem2, subindex2) => {
          return <h3 key={subindex2}>{subitem2.rule}</h3>
        })}
      </React.Fragment>
    );
  });
});

* I tried matching all the brackets but hopefully your IDE does a better job than I did in a plain text editor

Or using the implicit arrow function returns:

const content = assessmentData.map((item, idx) =>
  Object.entries(item).map(([key, value], subindex) => (
    <React.Fragment key={subindex}>
      <h2>{key}</h2>
      {value.map((subitem2, subindex2) => (
        <h3 key={subindex2}>{subitem2.rule}</h3>
      ))}
    </React.Fragment>
  ))
);

Also, as a solution for general question, you can propose a recursive traversal of an object or an array with variant depth, perhaps it will be useful to someone.

window.Fragment = React.Fragment
const assessmentData = [
    {
        "Sit1": [
            {
                "rule": "Rule1",
                "type": "High"
            }
        ]
    },
    {
        "Sit2": [
            {
                "rule": "Rule6",
                "type": "Low"
            }
        ]
    },
    {
        "another example:" : [
           {items: [1, 2]}, {other: [4, 5, 6]}
         ]
    }
]

const getObject = (obj, level) => {
  return Object.entries(obj).map(([key, val], i) => {
    return <Fragment key={`key${i}`}>{getTree(key, level + 1)}{getTree(val, level + 2)}</Fragment>
  })
}

const getArray = (arr, level) => {
  return arr.map((e, i) => {
    return <Fragment key={`key${i}`}>{getTree(e, level + 1)}</Fragment> 
  })
}

const getTree = (data, level=0) => {
  if (data instanceof Array) {
     return getArray(data, level)
  } else if (data instanceof Object) {
     return getObject(data, level) 
  }
  return <p style={{fontSize:`${20 - level}px`, paddingLeft: `${level}em`}}>{data}</p>      
}

const App = () => {
  return (
    <div>
      { getTree(assessmentData) }
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
* {
  margin: 0;
  padding: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Related