MarioD MarioD - 2 months ago 7
React JSX Question

Smarter way of counting values in object (JS, React)

Taking a look at the code below, is there a better way of getting the count of items that contain a certain key/value pair inside of a react state?

This method seems like it could cause a bottleneck once the list that I'm going through becomes large.

Here's a simplified example of the question at hand:



class App extends React.Component {
constructor() {
super();

this.state = {
animals: [
{type: 'cat'},
{type: 'dog'},
{type: 'cat'},
]
};
}

render() {
return(
<div className="app">
<Categories state={this.state} />
</div>
);
}
}

class Categories extends React.Component {
constructor() {
super();

this.countItems = this.countItems.bind(this);
}

countItems(type) {
var count = 0;

for(var i = 0; i < this.props.state.animals.length; i++) {
if(this.props.state.animals[i].type === type) {
count++;
}
}

return count;
}

render() {
return(
<div className="categories">
<div>Total animals: {this.props.state.animals.length}</div>
<div>Cats: {this.countItems('cat')}</div>
<div>Dogs: {this.countItems('dog')}</div>
</div>
);
}
}

ReactDOM.render(<App />, document.getElementById('container'));

<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="container"></div>




Answer

When this is a method you would call often, then it might be useful to index your data (animals) by type, and keep that updated whenever you make changes.

For instance:

In the App constructor you would create another property animalsPerType:

  constructor() {
    super();

    this.state = {
      animals: [
        {type: 'cat'},
        {type: 'dog'},
        {type: 'cat'},
      ]
    };
    this.state.animalsPerType = this.state.animals.reduce(function(acc, animal) {
        return acc.set(animal.type, (acc.get(animal.type) || []).concat(animal));
    }, new Map());
  }

Then your countItems method becomes trivial:

  countItems(type) {
    return this.props.state.animalsPerType.get(type).length;
  }