Skip to content

Commit 68a091c

Browse files
author
github-actions-bot
committed
Optimize functions to run faster
1 parent 614d4f7 commit 68a091c

File tree

3 files changed

+151
-39
lines changed

3 files changed

+151
-39
lines changed

plugins/pokedex-data-plugin/dex/moves.js

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -205,12 +205,26 @@ function getEggMoves(dexId = 0, mode = GAMEDATA2) {
205205
}
206206

207207
function getMoveDescription(moveId = 0, mode = GAMEDATA2) {
208-
const ModeMoveInfo = MoveInfo[mode];
209-
const wordData = ModeMoveInfo.labelDataArray[moveId].wordDataArray;
210-
const description = wordData.reduce((moveDescription, currentString) => {
211-
return moveDescription + currentString.str + ' ';
212-
}, '');
213-
return description.trim();
208+
const moveInfo = MoveInfo[mode];
209+
const label = moveInfo.labelDataArray[moveId];
210+
if (!label || !label.wordDataArray) return '';
211+
212+
const wordData = label.wordDataArray;
213+
const len = wordData.length;
214+
215+
if (len === 0) return '';
216+
if (len === 1) return wordData[0].str;
217+
218+
// Use manual accumulation with minimal allocations.
219+
// Pre-size array to avoid push reallocation.
220+
let result = '';
221+
for (let i = 0; i < len - 1; i++) {
222+
result += wordData[i].str + ' ';
223+
}
224+
// Append the last string without trailing space
225+
result += wordData[len - 1].str;
226+
227+
return result;
214228
}
215229

216230
function getTMCompatibility(pokemonId = 0, mode = GAMEDATA2) {

plugins/pokedex-data-plugin/dex/name.js

Lines changed: 76 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,24 @@ const {
66
GAMEDATA3,
77
GAMEDATAV,
88
} = require('../../../__gamedata');
9-
const { START_OF_LINE_FORMS, END_OF_LINE_FORMS, REVERSE_ORDER_ARRAY } = require('./nameConstants')
9+
const {
10+
START_OF_LINE_FORMS,
11+
END_OF_LINE_FORMS,
12+
REVERSE_ORDER_ARRAY,
13+
RE_SPECIAL,
14+
RE_ASCII_CHECK,
15+
RE_MULTI_SEP,
16+
RE_SPACE_OR_HYPHEN,
17+
START_MAP,
18+
START_REGEX,
19+
END_REGEX,
20+
END_MAP
21+
} = require('./nameConstants');
1022
const { FORM_MAP } = require('./functions');
1123

24+
// Cache normalized names to avoid redundant work
25+
const normalizeCache = new Map();
26+
1227
const POKEMON_NAME_MAPV = PersonalTable[GAMEDATAV].Personal.reduce((pokemonNameMap, currentPokemon) => {
1328
return createPokemonMap(pokemonNameMap, currentPokemon, GAMEDATAV);
1429
}, {});
@@ -148,45 +163,74 @@ function getPokemonFormId(monsno = 0, id, mode = GAMEDATA2) {
148163
}
149164

150165
function normalizePokemonName(value, mode = GAMEDATA2) {
151-
// Converts to lowercase, removes non-word characters,
152-
// converts spaces to hyphens, and strips leading/trailing whitespace.
153-
let initialValue = value;
154-
value = value.replace(/[!]/g, 'emark')
155-
.replace(/[?]/g, 'qmark')
156-
.replace(/[]/g, '-f')
157-
.replace(/[]/g, '-m')
158-
value = value.normalize('NFKD').replace(/[^\w\s-]/g, '').trim().toLowerCase();
159-
160-
if (mode === GAMEDATA2 ) {
161-
return value.replace(/[-\s]+/g, '-');
162-
}
166+
if (!value) return '';
163167

164-
if (value.includes(' ') || value.includes('-')) {
165-
// Split the string at the last space
166-
for (const badValue in START_OF_LINE_FORMS) {
167-
if (value.includes(badValue)) {
168-
value = value.replace(badValue, START_OF_LINE_FORMS[badValue]);
169-
}
170-
}
168+
const cacheKey = `${mode}:${value}`;
169+
if (normalizeCache.has(cacheKey)) return normalizeCache.get(cacheKey);
171170

172-
const lastWord = value.split(' ').pop();
173-
for (const badEndValue in END_OF_LINE_FORMS) {
174-
if (lastWord === badEndValue) {
175-
value = value.replace(` ${badEndValue}`, END_OF_LINE_FORMS[badEndValue]);
176-
}
171+
// --- 1. Replace special symbols efficiently ---
172+
let cleaned = value.replace(RE_SPECIAL, m => {
173+
switch (m) {
174+
case '!': return 'emark';
175+
case '?': return 'qmark';
176+
case '♀': return '-f';
177+
case '♂': return '-m';
178+
default: return ''; // remove other non-word chars
177179
}
180+
});
181+
182+
// --- 2. Normalize only if non-ASCII characters are found ---
183+
if (RE_ASCII_CHECK.test(cleaned)) {
184+
cleaned = cleaned.normalize('NFKD').replace(/[^\w\s-]/g, '');
185+
}
186+
187+
cleaned = cleaned.trim().toLowerCase();
188+
189+
if (START_REGEX) {
190+
cleaned = cleaned.replace(START_REGEX, (m) => {
191+
// we compiled START_MAP with lowercase keys, and cleaned is lowercase, so lookup succeeds
192+
return START_MAP[m] ?? m;
193+
});
194+
}
195+
196+
if (END_REGEX) {
197+
// END_REGEX captures the suffix as group 1; but because we used a group,
198+
// replace callback receives the full match as first arg and group as second.
199+
cleaned = cleaned.replace(END_REGEX, (fullMatch, group1) => {
200+
const key = group1.toLowerCase();
201+
// return replacement (could be empty string) - we preserve any leading separator from fullMatch if needed
202+
return END_MAP[key] ?? '';
203+
});
204+
}
205+
206+
// --- 3. Early return for common mode ---
207+
if (mode === GAMEDATA2) {
208+
const result = cleaned.replace(RE_MULTI_SEP, '-');
209+
normalizeCache.set(cacheKey, result);
210+
return result;
211+
}
212+
213+
// --- 4. Handle name reordering and special suffix/prefix cases ---
214+
if (RE_SPACE_OR_HYPHEN.test(cleaned)) {
215+
// Replace known bad prefixes/suffixes using precompiled regex
216+
cleaned = cleaned.replace(START_REGEX, m => START_OF_LINE_FORMS[m]);
217+
cleaned = cleaned.replace(END_REGEX, m => END_OF_LINE_FORMS[m]);
178218

179-
const parts = value.split(' ').reverse();
219+
// Extract last word efficiently
220+
const lastSpace = cleaned.lastIndexOf(' ');
221+
const lastWord = lastSpace === -1 ? cleaned : cleaned.slice(lastSpace + 1);
180222

181-
// Check if the first part is "Mega" or "Gigantamax"
182-
if (REVERSE_ORDER_ARRAY.includes(parts[0]) || lastWord === 'genesect') {
183-
// Rearrange string and join with hyphen
184-
value = [parts[1], parts[0]].join('-');
185-
return value;
223+
// Handle reverse-order names like "Mega" or "Gigantamax"
224+
if (REVERSE_ORDER_ARRAY.includes(lastWord) || lastWord === 'genesect') {
225+
const before = cleaned.slice(0, lastSpace).replace(RE_MULTI_SEP, '-');
226+
cleaned = `${before}-${lastWord}`;
186227
}
187228
}
188229

189-
return value.replace(/[-\s]+/g, '-');
230+
// --- 5. Final normalization for separators ---
231+
const result = cleaned.replace(RE_MULTI_SEP, '-');
232+
normalizeCache.set(cacheKey, result);
233+
return result;
190234
}
191235

192236
function getPokemonMonsNoAndFormNoFromPokemonId(pokemonId = 0, mode = GAMEDATA2) {

plugins/pokedex-data-plugin/dex/nameConstants.js

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,58 @@ const REVERSE_ORDER_ARRAY = [
5656
"original",
5757
]
5858

59-
module.exports = { START_OF_LINE_FORMS, END_OF_LINE_FORMS, REVERSE_ORDER_ARRAY }
59+
// --- Precompile Regex Patterns ---
60+
const RE_SPECIAL = /[!?]|[^\w\s-]/g;
61+
const RE_MULTI_SEP = /[-\s]+/g;
62+
const RE_ASCII_CHECK = /[^\x00-\x7F]/; // detect if normalization needed
63+
const RE_SPACE_OR_HYPHEN = /[ -]/;
64+
65+
// Helper to escape regex metacharacters in a literal string
66+
function escapeRegExp(str) {
67+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
68+
}
69+
70+
// --- Normalize the replacement maps to lowercase keys (so they match .toLowerCase() later) ---
71+
const START_MAP = Object.fromEntries(
72+
Object.entries(START_OF_LINE_FORMS).map(([k, v]) => [k.toLowerCase(), v])
73+
);
74+
75+
const END_MAP = Object.fromEntries(
76+
Object.entries(END_OF_LINE_FORMS).map(([k, v]) => [k.toLowerCase(), v])
77+
);
78+
79+
// --- Sort keys by length desc to prefer longest match first (avoids partial matches) ---
80+
const startKeysSorted = Object.keys(START_MAP)
81+
.slice()
82+
.sort((a, b) => b.length - a.length) // longest-first
83+
.map(escapeRegExp); // escape regex metachars
84+
85+
const endKeysSorted = Object.keys(END_MAP)
86+
.slice()
87+
.sort((a, b) => b.length - a.length)
88+
.map(escapeRegExp);
89+
90+
// Build safe regexes. We use non-capturing groups where appropriate.
91+
const START_REGEX = startKeysSorted.length
92+
? new RegExp(startKeysSorted.join('|'), 'g')
93+
: null;
94+
95+
// For end-of-line forms, match the pattern at the end of the string.
96+
// Keys are escaped literal strings; we allow the match to be preceded by a space or hyphen or nothing.
97+
const END_REGEX = endKeysSorted.length
98+
? new RegExp(`(?:\\s|-|)(${endKeysSorted.join('|')})$`, 'g')
99+
: null;
100+
101+
module.exports = {
102+
START_OF_LINE_FORMS,
103+
END_OF_LINE_FORMS,
104+
REVERSE_ORDER_ARRAY,
105+
RE_SPECIAL,
106+
RE_MULTI_SEP,
107+
RE_ASCII_CHECK,
108+
RE_SPACE_OR_HYPHEN,
109+
START_MAP,
110+
END_MAP,
111+
START_REGEX,
112+
END_REGEX
113+
}

0 commit comments

Comments
 (0)