3
\$\begingroup\$

I have the following code in my React app that creates tags out of JSON data:

const files = Object.values(gist.files);
const tags = files.map((file) => {
    let tag = file.language ? file.language : 'Plain Text';
    return tag;
});
let unique = [...new Set(tags)]; 
const unique_tags = unique.map((tag) => {
    return <Tag value={tag}/>;
});

The code starts by creating an array out of the files' JSON object, next it maps through the files array and returns the value of the language key (Javascript, C, etc.) and if its not undefined, returns plaintext. After that, it filters so there are only unique tags. Finally, it maps through the unique tags and returns a React component.

As you can see, there are two maps and one filter. I feel like this could be done more efficiently.

The JSON is coming from the GitHub Gist API.
For example: https://api.github.com/gists/25c75fa76cf151568d12

\$\endgroup\$
  • 1
    \$\begingroup\$ Unless you've explicitly profiled this code to show that it is a performance bottleneck, worrying about optimizing this code is probably not worth your time. \$\endgroup\$ – loganfsmyth Aug 11 '17 at 17:24
  • \$\begingroup\$ I added more details to my answer and why mine should perform better than the original code as well as the other given answers. \$\endgroup\$ – styfle Aug 13 '17 at 18:22
1
\$\begingroup\$

One thing to note about your code is that the array of files is looped over many times so depending on the size of this array, it could be slow.

Here's the places where I see a loop:

  • Line 1: Object.values(gist.files) loops over each file object property and returns it's value
  • Line 2: files.map(file) loops over each file value to return a string
  • Line 6: new Set(tags) loops over each value in the array to get unique values
  • Line 6: [...set] loops over each value in the set to create a "Shallow Clone" of as an Array
  • Line 7 unique.map loops over each value in the unique Array to return an array of JSX Nodes

Now the time complexity is only O(n) in your example code so it will scale but you can improve the speed by roughly 5x by doing a single loop instead of 5.

Below is a single loop with time complexity of O(n).

function getTagElements(gist) {
  const tags = new Set();
  const arr = [];

  for (let file of gist.files) {
      const tag = file.language ? file.language : 'Plain Text';
      if (!tags.has(tag)) {
          arr.push(<Tag value={tag}/>);
          tags.add(tag);
      }
  }
  return arr;
}



function Tag(props) {
    // render your tag
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.5.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.5.0/react-dom.min.js"></script>

Note that adding an item to a set is typically an O(1) operation (constant time operation).

Attempting to use Array.indexOf is a O(n) operation and will not scale well with many values.

\$\endgroup\$
1
\$\begingroup\$

You can use a for..of loop to avoid using .map() twice and Set

const files = Object.values(gist.files);
const unique_tags = []; 

for (let file of files) {
  let tag = file.language ? file.language : 'Plain Text';
  if (unique_tags.indexOf(<Tag value={tag}/>) === -1) {
    unique_tags = [...unique_tags, <Tag value={tag}/>]
  }
}
\$\endgroup\$
  • \$\begingroup\$ Note that Array.indexOf is an O(n) operation so this algorithm is O(n^2) and will be noticeably slower than my answer for large lists of files. \$\endgroup\$ – styfle Aug 13 '17 at 18:11
0
\$\begingroup\$

If you want to improve the readability of this code, you can write it like:

const tags = files
    .map(x => x.language || 'Plain Text')
    .filter(x => files.indexOf(x) == files.lastIndexOf(x))
    .map(x => <Tag value={x});

Then render tags using {tags}

However, I am not sure you can do something with respect to "efficiency", except styfle's solution.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.