3

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?

Souvik Ray
  • 2,899
  • 5
  • 38
  • 70

2 Answers2

2

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>
  ))
);
Souvik Ray
  • 2,899
  • 5
  • 38
  • 70
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • 1
    thanks for highlighting that out. So the trick is for every loop, I need to return the output right? – Souvik Ray Dec 21 '21 at 05:32
  • 1
    @SouvikRay Affirmative. When mapping an array from one set of values to the other, you need to return the values you are mapping to. This includes if the values you are mapping to also are arrays that need to map to other values. – Drew Reese Dec 21 '21 at 05:36
0

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>
Daniil Loban
  • 4,165
  • 1
  • 14
  • 20