Skip to content

Commit

Permalink
Replace fuse.js with levenshtein distance algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
alexemanuelol committed Dec 11, 2023
1 parent 3aeb13e commit fb28595
Show file tree
Hide file tree
Showing 12 changed files with 9,546 additions and 9,163 deletions.
18,559 changes: 9,452 additions & 9,107 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
"discord-api-types": "^0.37.37",
"discord.js": "^14.8.0",
"ffmpeg-static": "^5.1.0",
"fuse.js": "^6.6.2",
"gm": "^1.25.0",
"jimp": "^0.22.7",
"libsodium-wrappers": "^0.7.11",
Expand All @@ -43,4 +42,4 @@
"jpeg-js": "0.4.4",
"protobufjs": "7.2.4"
}
}
}
2 changes: 1 addition & 1 deletion src/commands/craft.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ module.exports = {
let itemId = null;
if (craftItemName !== null) {
const item = client.items.getClosestItemIdByName(craftItemName)
if (item === undefined) {
if (item === null) {
const str = client.intlGet(guildId, 'noItemWithNameFound', {
name: craftItemName
});
Expand Down
2 changes: 1 addition & 1 deletion src/commands/despawn.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ module.exports = {
let itemId = null;
if (despawnItemName !== null) {
const item = client.items.getClosestItemIdByName(despawnItemName)
if (item === undefined) {
if (item === null) {
const str = client.intlGet(guildId, 'noItemWithNameFound', {
name: despawnItemName
});
Expand Down
6 changes: 3 additions & 3 deletions src/commands/market.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ module.exports = {
let itemId = null;
if (searchItemName !== null) {
const item = client.items.getClosestItemIdByName(searchItemName)
if (item === undefined) {
if (item === null) {
const str = client.intlGet(interaction.guildId, 'noItemWithNameFound', {
name: searchItemName
});
Expand Down Expand Up @@ -236,7 +236,7 @@ module.exports = {
let itemId = null;
if (subscribeItemName !== null) {
const item = client.items.getClosestItemIdByName(subscribeItemName)
if (item === undefined) {
if (item === null) {
const str = client.intlGet(interaction.guildId, 'noItemWithNameFound', {
name: subscribeItemName
});
Expand Down Expand Up @@ -304,7 +304,7 @@ module.exports = {
let itemId = null;
if (subscribeItemName !== null) {
const item = client.items.getClosestItemIdByName(subscribeItemName)
if (item === undefined) {
if (item === null) {
const str = client.intlGet(interaction.guildId, 'noItemWithNameFound', {
name: subscribeItemName
});
Expand Down
2 changes: 1 addition & 1 deletion src/commands/recycle.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ module.exports = {
let itemId = null;
if (recycleItemName !== null) {
const item = client.items.getClosestItemIdByName(recycleItemName)
if (item === undefined) {
if (item === null) {
const str = client.intlGet(guildId, 'noItemWithNameFound', {
name: recycleItemName
});
Expand Down
2 changes: 1 addition & 1 deletion src/commands/research.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ module.exports = {
let itemId = null;
if (researchItemName !== null) {
const item = client.items.getClosestItemIdByName(researchItemName)
if (item === undefined) {
if (item === null) {
const str = client.intlGet(guildId, 'noItemWithNameFound', {
name: researchItemName
});
Expand Down
2 changes: 1 addition & 1 deletion src/commands/stack.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ module.exports = {
let itemId = null;
if (stackItemName !== null) {
const item = client.items.getClosestItemIdByName(stackItemName)
if (item === undefined) {
if (item === null) {
const str = client.intlGet(guildId, 'noItemWithNameFound', {
name: stackItemName
});
Expand Down
28 changes: 9 additions & 19 deletions src/structures/Items.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,19 @@
const Fs = require('fs');
const Path = require('path');

const Fuse = require('fuse.js')
const Utils = require('../util/utils.js');

class Items {
constructor() {
this._items = JSON.parse(Fs.readFileSync(
Path.join(__dirname, '..', 'staticFiles', 'items.json'), 'utf8'));

const flattenedItems = Object.keys(this.items).map(id => ({ id, ...this.items[id] }));
this._fuse = new Fuse(flattenedItems, {
keys: [{
name: 'name',
weight: 0.7,
}, {
name: 'shortname',
weight: 0.3,
}]
});
this._itemNames = Object.values(this.items).map(item => item.name);
}

/* Getters and Setters */
/* Getters */
get items() { return this._items; }
set items(items) { this._items = items; }
get fuse() { return this._fuse; }
set fuse(fuse) { this._fuse = fuse; }
get itemNames() { return this._itemNames; }

addItem(id, content) { this.items[id] = content; }
removeItem(id) { delete this.items[id]; }
Expand All @@ -70,11 +59,12 @@ class Items {
}

getClosestItemIdByName(name) {
const result = this.fuse.search(name)[0];
if (result) {
return this.fuse.search(name)[0].item.id;
const closestString = Utils.findClosestString(name, this.itemNames);
if (closestString !== null) {
const id = Object.entries(this.items).find(([key, value]) => value.name === closestString);
return id ? id[0] : null;
}
return undefined;
return null;
}
}

Expand Down
34 changes: 15 additions & 19 deletions src/structures/RustLabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
*/

const Fuse = require('fuse.js')

const Items = require('./Items');
const RustlabsBuildingBlocks = require('../staticFiles/rustlabsBuildingBlocks.json');
const RustlabsOther = require('../staticFiles/rustlabsOther.json');
Expand All @@ -32,6 +30,7 @@ const SmeltingData = require('../staticFiles/rustlabsSmeltingData.json');
const DespawnData = require('../staticFiles/rustlabsDespawnData.json');
const StackData = require('../staticFiles/rustlabsStackData.json');
const DecayData = require('../staticFiles/rustlabsDecayData.json');
const Utils = require('../util/utils.js');

const IGNORED_RECYCLE_ITEMS = [
'-946369541' /* Low Grade Fuel */
Expand Down Expand Up @@ -85,11 +84,8 @@ class RustLabs {
'sulfurLowFirst'
];

const flattenedBuildingBlocks = Object.keys(this.rustlabsBuildingBlocks).map(e => ({ ['name']: e }));
this._fuseBuildingBlocks = new Fuse(flattenedBuildingBlocks, { keys: [{ name: 'name', weight: 0.1 }] });

const flattenedOther = Object.keys(this.rustlabsOther).map(e => ({ ['name']: e }));
this._fuseOther = new Fuse(flattenedOther, { keys: [{ name: 'name', weight: 0.1 }] });
this._buildingBlocks = Object.keys(this.rustlabsBuildingBlocks);
this._other = Object.keys(this.rustlabsOther);
}


Expand All @@ -111,8 +107,8 @@ class RustLabs {
get durabilityGroups() { return this._durabilityGroups }
get durabilityWhich() { return this._durabilityWhich; }
get orderedBy() { return this._orderedBy; }
get fuseBuildingBlocks() { return this._fuseBuildingBlocks; }
get fuseOther() { return this._fuseOther; }
get buildingBlocks() { return this._buildingBlocks; }
get other() { return this._other; }


/***********************************************************************************
Expand Down Expand Up @@ -146,27 +142,27 @@ class RustLabs {
/**
* Get the closest building block name by name.
* @param {string} name The name of the building block.
* @return {string|undefined} undefined if the building block couldnt be found, otherwise the closest name.
* @return {string|null} null if the building block couldnt be found, otherwise the closest name.
*/
getClosestBuildingBlockNameByName(name) {
const result = this.fuseBuildingBlocks.search(name);
if (result.length !== 0) {
return result[0].item.name;
const closestString = Utils.findClosestString(name, this.buildingBlocks);
if (closestString !== null) {
return closestString;
}
return undefined;
return null;
}

/**
* Get the closest other name by name.
* @param {string} name The name of the other.
* @return {string|undefined} undefined if the other couldnt be found, otherwise the closest name.
* @return {string|null} null if the other couldnt be found, otherwise the closest name.
*/
getClosestOtherNameByName(name) {
const result = this.fuseOther.search(name);
if (result.length !== 0) {
return result[0].item.name;
const closestString = Utils.findClosestString(name, this.other);
if (closestString !== null) {
return closestString;
}
return undefined;
return null;
}

/**
Expand Down
16 changes: 8 additions & 8 deletions src/structures/RustPlus.js
Original file line number Diff line number Diff line change
Expand Up @@ -934,7 +934,7 @@ class RustPlus extends RustPlusLib {
}

const item = Client.client.items.getClosestItemIdByName(itemSearchName)
if (item === undefined || itemSearchName === '') {
if (item === null || itemSearchName === '') {
const str = Client.client.intlGet(this.guildId, 'noItemWithNameFound', {
name: itemSearchName
});
Expand Down Expand Up @@ -1178,7 +1178,7 @@ class RustPlus extends RustPlusLib {
}

const itemId = Client.client.items.getClosestItemIdByName(command);
if (itemId === undefined) {
if (itemId === null) {
return Client.client.intlGet(this.guildId, 'noItemWithNameFound', {
name: command
});
Expand Down Expand Up @@ -1650,7 +1650,7 @@ class RustPlus extends RustPlusLib {
}

const itemId = Client.client.items.getClosestItemIdByName(name);
if (itemId === undefined) {
if (itemId === null) {
return Client.client.intlGet(this.guildId, 'noItemWithNameFound', {
name: name
});
Expand Down Expand Up @@ -1696,7 +1696,7 @@ class RustPlus extends RustPlusLib {
}

const itemId = Client.client.items.getClosestItemIdByName(name);
if (itemId === undefined) {
if (itemId === null) {
return Client.client.intlGet(this.guildId, 'noItemWithNameFound', {
name: name
});
Expand Down Expand Up @@ -1729,7 +1729,7 @@ class RustPlus extends RustPlusLib {
}

const itemId = Client.client.items.getClosestItemIdByName(name);
if (itemId === undefined) {
if (itemId === null) {
return Client.client.intlGet(this.guildId, 'noItemWithNameFound', {
name: name
});
Expand Down Expand Up @@ -2100,7 +2100,7 @@ class RustPlus extends RustPlusLib {
}

const item = Client.client.items.getClosestItemIdByName(itemSearchName)
if (item === undefined || itemSearchName === '') {
if (item === null || itemSearchName === '') {
const str = Client.client.intlGet(this.guildId, 'noItemWithNameFound', {
name: itemSearchName
});
Expand Down Expand Up @@ -2146,7 +2146,7 @@ class RustPlus extends RustPlusLib {
const itemResearchName = command;

const item = Client.client.items.getClosestItemIdByName(itemResearchName)
if (item === undefined || itemResearchName === '') {
if (item === null || itemResearchName === '') {
const str = Client.client.intlGet(this.guildId, 'noItemWithNameFound', {
name: itemResearchName
});
Expand Down Expand Up @@ -2289,7 +2289,7 @@ class RustPlus extends RustPlusLib {
}

const itemId = Client.client.items.getClosestItemIdByName(command);
if (itemId === undefined) {
if (itemId === null) {
return Client.client.intlGet(this.guildId, 'noItemWithNameFound', {
name: command
});
Expand Down
53 changes: 53 additions & 0 deletions src/util/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,57 @@ module.exports = {
str = str.replace(/[\u200B-\u200D\uFEFF]/g, '');
return str.replace(/[\u0000-\u001F\u007F-\u009F]/g, "");
},

findClosestString: function (string, array, threshold = 2) {
let minDistance = Infinity;
let closestString = null;

for (let i = 0; i < array.length; i++) {
const currentString = array[i];
const distance = levenshteinDistance(string, currentString);

if (distance < minDistance) {
minDistance = distance;
closestString = currentString;
}

if (minDistance === 0) break;
}

return minDistance > threshold ? null : closestString;
},
}

/* Function to calculate Levenshtein distance between two strings */
function levenshteinDistance(s1, s2) {
s1 = s1.toLowerCase();
s2 = s2.toLowerCase();

const m = s1.length;
const n = s2.length;
const dp = [];

for (let i = 0; i <= m; i++) {
dp[i] = [i];
}
for (let j = 0; j <= n; j++) {
dp[0][j] = j;
}

for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (s1[i - 1] === s2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
}
else {
dp[i][j] = 1 + Math.min(
dp[i - 1][j],
dp[i][j - 1],
dp[i - 1][j - 1]
);
}
}
}

return dp[m][n];
}

0 comments on commit fb28595

Please sign in to comment.