Bugs and bugfixes in your code
You have two main problems. One of them is i++ which you have addressed already. i++ assigns i a new value (of i + 1) and as it is postfix notation (in contrast to prefix notation ++i) this value is only assigned after it is used, that explains why the word came is shown twice and the frequency part is correct. But as you increment the value again in textObj[i++].frequency you update i once more creating even more mess.
Conclusion: Don't update i, just use i + 1 and it should work.
Now one other problem is when you use i + 1 you will run out of bounds of the array when i = textObj.length - 1, that's why it says cannot read 'element'. To avoid this you have to change your condition in the for loop to let i = 0; i < textObj.length - 1; i += 2.
Fix bugs in a more idiomatic way
Having said this, you should consider a more idiomatic approach to do this, similar to what you will see in many React applications.
You can do this by using map() which will return an array of all the values returned in the callback.
const textObjData = [
{ element: "came", frequency: 3 },
{ element: "here", frequency: 2 },
{ element: "i", frequency: 1 },
];
function Result() {
const [textObj, setTextObj] = React.useState(textObjData);
return (
<React.Fragment>
{textObj.length !== 0 ? (
// map takes three arguments here, the item in the array, the index and the array itself
// we are using object destructuring to get the values of element and frequency so we don't have to deal with indices all the time
textObj.map(({ element, frequency }, idx, elements) => {
// check if we would run out of bounds for alst element, if yes => tell React to not render anyting by returning null
if (idx === elements.length - 1) return null;
// we are not out of bounds, so get next element and desctructur it as well
// we need to assign new names to the destructured value to avoid naming conflicts with above destructured values
const { element: nextElement, frequency: nextFrequency } = elements[idx + 1];
return (
<div>
<h1>Most frequent word was:</h1>
<h2>
'{element}' for {frequency} times
</h2>
<h1>Second most frequent word was:</h1>
<h2>
'{nextElement}' for {nextFrequency} times
</h2>
</div>
);
})
) : (
<div></div>
)}
</React.Fragment>
);
}
ReactDOM.render(<Result/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Design issue
Now looking at the snippet you will notice that it still prints "here" twice and that is due to an design issue you have. You say you want to only show the two words with most occurrences but you still loop over the whole array, so it first displays the values with index 0,1 then 1,2 then 2,3 then 3,4 and so on. But as you only have 3 values only the one with index 1 ("here") gets printed twice.
A possible solution
If you only want to show the two elements with the most occurrences there is no need to get the next element. Just use slice(0, 2) in conjunction with map() to get at most two elements rendered.
Please note: I have factored out the logic to show the word frequency to another component to get more concise and readable code.
const textObjData = [
{ element: "came", frequency: 3 },
{ element: "here", frequency: 2 },
{ element: "i", frequency: 1 },
];
function Result() {
const [textObj, setTextObj] = React.useState(textObjData);
return (
<React.Fragment>
{textObj.length !== 0 ?
// we are using object destructuring to get the values of element and frequency so we don't have to deal with indices all the time
textObj.slice(0, 2).map(({ element, frequency }, idx) => (
<WordFrequency
key={element}
element={element}
frequency={frequency}
rank={idx + 1}
/>
))
: <div></div>}
</React.Fragment>
);
}
function WordFrequency({ element, frequency, rank }) {
const mapping = {
1: "M",
2: "Second m",
}
return (
<div>
<h1>{mapping[rank]}ost frequent word was:</h1>
<h2>
'{element}' for {frequency} times
</h2>
</div>
);
}
ReactDOM.render(<Result/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
If you actually want to show a list of words with most occurrences you could just remove the slice() and it will work as well.
const textObjData = [
{ element: "came", frequency: 3 },
{ element: "here", frequency: 2 },
{ element: "i", frequency: 1 },
];
function Result() {
const [textObj, setTextObj] = React.useState(textObjData);
return (
<React.Fragment>
{textObj.length !== 0 ?
// we are using object destructuring to get the values of element and frequency so we don't have to deal with indices all the time
textObj.map(({ element, frequency }, idx) => (
<WordFrequency
key={element}
element={element}
frequency={frequency}
rank={idx + 1}
/>
))
: <div></div>}
</React.Fragment>
);
}
function WordFrequency({ element, frequency, rank }) {
return (
<div>
<h1>#{rank} most frequent word was:</h1>
<h2>
'{element}' for {frequency} times
</h2>
</div>
);
}
ReactDOM.render(<Result/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Please note: I am assuming, as your question suggests, that the elements in your array are already sorted in descending order.
FIRSTand theSECONDalone, one need not necessarily useforloop (or any loop). Instead, simply usingtextObj[0]andtextObj[1]would suffice. So,<h1>Most frequent... <h2>{textObj[0].element} for {textObj[1].frequency} times </h2> <h1>Second most... <h2> {textObj[1].element} ....should get the desired result.