123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- "use strict";
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- const similarity = (a, b) => {
- const l = Math.min(a.length, b.length);
- let dist = 0;
- for (let i = 0; i < l; i++) {
- const ca = a.charCodeAt(i);
- const cb = b.charCodeAt(i);
- dist += Math.max(0, 10 - Math.abs(ca - cb));
- }
- return dist;
- };
-
-
- const getName = (a, b) => {
- const l = Math.min(a.length, b.length);
- let r = "";
- for (let i = 0; i < l; i++) {
- const ca = a.charAt(i);
- const cb = b.charAt(i);
- r += ca;
- if (ca === cb) {
- continue;
- }
- return r;
- }
- return a;
- };
-
-
- class Node {
-
-
- constructor(item, key, size) {
- this.item = item;
- this.key = key;
- this.size = size;
- }
- }
-
-
- class Group {
-
-
- constructor(nodes, similarities) {
- this.nodes = nodes;
- this.similarities = similarities;
- this.size = nodes.reduce((size, node) => size + node.size, 0);
-
- this.key = undefined;
- }
- }
-
-
-
-
-
-
- module.exports = ({ maxSize, minSize, items, getSize, getKey }) => {
-
- const result = [];
-
- const nodes = Array.from(
- items,
- item => new Node(item, getKey(item), getSize(item))
- );
-
-
- const initialNodes = [];
-
-
- nodes.sort((a, b) => {
- if (a.key < b.key) return -1;
- if (a.key > b.key) return 1;
- return 0;
- });
-
-
- for (const node of nodes) {
- if (node.size >= maxSize) {
- result.push(new Group([node], []));
- } else {
- initialNodes.push(node);
- }
- }
-
- if (initialNodes.length > 0) {
-
-
- const similarities = [];
- for (let i = 1; i < initialNodes.length; i++) {
- const a = initialNodes[i - 1];
- const b = initialNodes[i];
- similarities.push(similarity(a.key, b.key));
- }
-
- const initialGroup = new Group(initialNodes, similarities);
-
- if (initialGroup.size < minSize) {
-
-
- if (result.length > 0) {
- const smallestGroup = result.reduce((min, group) =>
- min.size > group.size ? group : min
- );
- for (const node of initialGroup.nodes) smallestGroup.nodes.push(node);
- smallestGroup.nodes.sort((a, b) => {
- if (a.key < b.key) return -1;
- if (a.key > b.key) return 1;
- return 0;
- });
- } else {
-
-
- result.push(initialGroup);
- }
- } else {
- const queue = [initialGroup];
-
- while (queue.length) {
- const group = queue.pop();
-
- if (group.size < maxSize) {
- result.push(group);
- continue;
- }
-
-
-
-
- let left = 0;
- let leftSize = 0;
- while (leftSize <= minSize) {
- leftSize += group.nodes[left].size;
- left++;
- }
- let right = group.nodes.length - 1;
- let rightSize = 0;
- while (rightSize <= minSize) {
- rightSize += group.nodes[right].size;
- right--;
- }
-
- if (left - 1 > right) {
-
-
-
-
- result.push(group);
- continue;
- }
- if (left <= right) {
-
-
-
-
- let best = left - 1;
- let bestSimilarity = group.similarities[best];
- for (let i = left; i <= right; i++) {
- const similarity = group.similarities[i];
- if (similarity < bestSimilarity) {
- best = i;
- bestSimilarity = similarity;
- }
- }
- left = best + 1;
- right = best;
- }
-
-
-
- const rightNodes = [group.nodes[right + 1]];
-
- const rightSimilaries = [];
- for (let i = right + 2; i < group.nodes.length; i++) {
- rightSimilaries.push(group.similarities[i - 1]);
- rightNodes.push(group.nodes[i]);
- }
- queue.push(new Group(rightNodes, rightSimilaries));
-
- const leftNodes = [group.nodes[0]];
-
- const leftSimilaries = [];
- for (let i = 1; i < left; i++) {
- leftSimilaries.push(group.similarities[i - 1]);
- leftNodes.push(group.nodes[i]);
- }
- queue.push(new Group(leftNodes, leftSimilaries));
- }
- }
- }
-
-
- result.sort((a, b) => {
- if (a.nodes[0].key < b.nodes[0].key) return -1;
- if (a.nodes[0].key > b.nodes[0].key) return 1;
- return 0;
- });
-
-
- for (let i = 0; i < result.length; i++) {
- const group = result[i];
- const first = group.nodes[0];
- const last = group.nodes[group.nodes.length - 1];
- let name = getName(first.key, last.key);
- group.key = name;
- }
-
-
- return result.map(group => {
-
- return {
- key: group.key,
- items: group.nodes.map(node => node.item),
- size: group.size
- };
- });
- };
|