0

I have the following array:

0: {element: 'came', frequency: 3}
1: {element: 'here', frequency: 2}
2: {element: 'i', frequency: 1}

Each element is an object that shows the frequency of words in a text.

I want to do something like "The most frequent word was X, for Y times. The second most frequent word was Z, for D times.

So i did this. textObj is the array with the objects.

function Result() {
    const results = [];
    if (textObj.length != 0) {
      for (let i = 0; i < textObj.length; i += 2) {
        results.push(
          <div>
            <h1>Most frequent word was:</h1>
            <h2>
              '{textObj[i].element}' for {textObj[i].frequency} times
            </h2>
            <h1>Second most frequent word was:</h1>
            <h2>
              '{textObj[i++].element}' for {textObj[i++].frequency} times
            </h2>
          </div>
        );
      }

      return results;
    }

    return <div></div>;
  }

And then i render it returning in the main component.

In the above example, i have the phrase "I came came came here here". For some reason, it's showing only the word "came" in both . But the frequency part is correct, it's showing 3 and then 2. I tried instead of textObj[i++], textObj[i+1], but it says that cannot read 'element'.

What am i doing wrong?

1
  • If the objective is to only display the FIRST and the SECOND alone, one need not necessarily use for loop (or any loop). Instead, simply using textObj[0] and textObj[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. Commented Mar 31, 2022 at 13:03

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

Comments

1

Most frequently used word and second most frequently used word can be done by sorting the array and getting the first and second elements

var arr =  [{element: 'came', frequency: 3},
{element: 'here', frequency: 2},
{element: 'i', frequency: 1}]
var sortedArr = arr.sort((a, b) => b.frequency - a.frequency);

var mostFrequencWord = sortedArr[0];
var mostSecondFrequencWord = sortedArr[1];
console.log(`The most frequent word was ${mostFrequencWord.element}, for ${mostFrequencWord.frequency} time: `)
console.log(`The most frequent second word was ${mostSecondFrequencWord.element}, for ${mostSecondFrequencWord.frequency} time: `)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.