1
\$\begingroup\$

I have a list of JSON objects that looks like this:

[{
  url: '/home',
  ...
}, {
  url: '/analytics',
  ...
}]

I'm using the following function to count the frequencies of the URLs in order to pass it to a c3.js chart.

var getSectionFrequencyData = function(data) {
  var frequencies = {};
  data.map(function(entry) {
    var matches = /[a-z]+/g.exec(entry.url);
    var url = 'home';
    if (matches) {
      url = matches[0];
    }
    frequencies[url] = frequencies[url] ? frequencies[url] + 1 : 1;
  });
  var items = Object.keys(frequencies).map(function(section) {
    return [section, frequencies[section]];
  }).sort(function(a, b) {
    return b[1] - a[1];
  }).slice(0, 10);
  return [
    ['sections'].concat(items.map(item => item[0])),
    ['frequency'].concat(items.map(item => item[1]))
  ];
};

This code works, but the frequency counting, sorting, and grouping seems extremely overengineered and clunky. Best practices for this?

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

The approach in your code is straightforward and hence easy to understand.

Nits:

  • use more of ES2015+ if you use some parts of it anyway - for example Map is handy here;
  • don't mix function and => notations - unless the former is absolutely required;
  • don't use array .map if you don't need the output array - to avoid garbage collection lags;

const getSectionFrequencyData = data => {
  const getSection = entry => /[a-z]+|$/.exec(entry.url || '')[0];

  const frequencies = new Map();
  for (const entry of data) {
    const section = getSection(entry) || 'home';
    frequencies.set(section, (frequencies.get(section) || 0) + 1);
  }

  const sorted10 = new Map(
    [...frequencies.entries()]
      .sort((a, b) => b[1] - a[1])
      .slice(0, 10)
  );

  return [
    ['sections', ...sorted10.keys()],
    ['frequency', ...sorted10.values()],
  ];
};
\$\endgroup\$
  • \$\begingroup\$ Thanks for the answer. I'm super intrigued though, because it contains so many things that I haven't seen before or consider as evil hacks. Using Map seems very elegant for the last part. I'll accept your answer for now and leave further questions about this magic later. \$\endgroup\$ – omgimanerd Jul 7 '17 at 20:15
  • \$\begingroup\$ The only hack I've used is [a-z]+|$ to make exec produce at least an empty string (I thought it's quite an obvious regex). What else? || 0 is a norm, ... is a ES2015 spread syntax. \$\endgroup\$ – wOxxOm Jul 7 '17 at 20:23
  • \$\begingroup\$ I didn't know about that regex trick and I didn't know about the existence of Map. I knew about spread syntax and || 0. I prefer to avoid || 0 because it can behave differently when not using numbers. Using spread syntax to create a copy seemed interesting and hackish. After reading the MDN docs though, it seems like that's an intended use case for it. ¯_(ツ)_/¯ \$\endgroup\$ – omgimanerd Jul 7 '17 at 20:30
  • \$\begingroup\$ || 0 is okay in a fully controlled case. Otherwise even integerVar++ in a for loop may be called a hack. \$\endgroup\$ – wOxxOm Jul 7 '17 at 20:35

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.