ujh/fountainpencompanion

View on GitHub
app/javascript/src/admin/pens-micro-clusters/withDistance.js

Summary

Maintainability
A
3 hrs
Test Coverage
import levenshtein from "fast-levenshtein";

// This is the most expensive computation in this app. Group inks by name first
// and only compare between those that are really different.
export const withDistance = (macroClusters, activeCluster) => {
  const activeGroupedInks = activeCluster.grouped_entries;
  return macroClusters.map((c) => {
    const macroClusterInks = c.grouped_entries.concat(c);
    return {
      ...c,
      distance: dist(macroClusterInks, activeGroupedInks)
    };
  });
};

const dist = (macroClusterInks, microClusterInks) => {
  const calc1 = (c1, c2) =>
    minLev(c1.brand, c2.brand) + minLev(c1.model, c2.model);
  const calc2 = (c1, c2) =>
    minLev([c1.brand, c1.model].join(""), [c2.brand, c2.model].join(""));
  const calc3 = (c1, c2) =>
    minLev(c1.brand, c2.brand) + minLev(c1.model, c2.model);

  let minDistance = Number.MAX_SAFE_INTEGER;
  macroClusterInks.forEach((ci1) => {
    microClusterInks.forEach((ci2) => {
      const dist = Math.min(
        ...[calc1(ci1, ci2), calc2(ci1, ci2), calc3(ci1, ci2)]
      );
      if (dist < minDistance) minDistance = dist;
    });
  });
  return minDistance;
};

const minLev = (str1, str2) => {
  return Math.min(
    levenshtein.get(str1, str2),
    levenshtein.get(stripped(str1), stripped(str2))
  );
};

const stripped = (str) => {
  return str
    .replace(/-/i, "")
    .replace(/(\([^)]*\))/i, "")
    .replace(/\s+/i, "");
};