@acusti/uniquify
Version:
Generate a unique string (e.g. for filenames or url slugs) using an incremental counter
144 lines (143 loc) • 4.2 kB
JavaScript
const escapeRegExp = (str) => str.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
const getIn = (item, path) => {
let value = item;
for (let i = 0; i < path.length && value != null; i++) {
value = value[path[i]];
}
return value;
};
const propertyValueExists = ({
caseSensitive,
items,
propertyPath,
propertyPathAlternate,
value
}) => {
value = caseSensitive ? value : value.toLowerCase();
return items.some((item) => {
if (item == null) return false;
let itemPropertyValue = propertyPath ? getIn(item, propertyPath) : item;
if (itemPropertyValue == null && propertyPathAlternate) {
itemPropertyValue = getIn(item, propertyPathAlternate);
}
if (typeof itemPropertyValue !== "string") {
return false;
}
if (caseSensitive) {
return itemPropertyValue === value;
}
return itemPropertyValue.toLowerCase() === value;
});
};
const counterRegexString = "(?:[1-9][0-9]|[2-9])";
const counterFromOneRegexString = "(?:[1-9][0-9]|[1-9])";
const otherInstanceExists = ({
caseSensitive,
items,
propertyPath,
propertyPathAlternate,
separator,
value,
valueStem
}) => {
const existingRegexString = `^${escapeRegExp(
valueStem
)}(?:${separator}${counterRegexString})?$`;
const existingRegex = caseSensitive ? new RegExp(existingRegexString) : new RegExp(existingRegexString, "i");
return items.some((item) => {
if (item == null) return false;
let itemPropertyValue = propertyPath ? getIn(item, propertyPath) : item;
if (itemPropertyValue == null && propertyPathAlternate) {
itemPropertyValue = getIn(item, propertyPathAlternate);
}
if (typeof itemPropertyValue !== "string") {
return false;
}
if (itemPropertyValue.toLowerCase() === value) {
return false;
}
return existingRegex.test(itemPropertyValue);
});
};
const uniquify = ({
caseSensitive = false,
identify1AsCounter = false,
isSuffixOptional = false,
items,
propertyPath,
propertyPathAlternate,
separator = " ",
suffix = "",
value
}) => {
const valueIdxRegexString = `${separator}${identify1AsCounter ? counterFromOneRegexString : counterRegexString}$`;
const valueIdxRegex = caseSensitive ? new RegExp(valueIdxRegexString) : new RegExp(valueIdxRegexString, "i");
let suffixRegexString = "";
let suffixRegex = null;
if (suffix) {
const optionalValueIdxRegexString = `(?:${valueIdxRegexString.slice(0, -1)})?$`;
suffixRegexString = `${separator}${escapeRegExp(
suffix
)}${optionalValueIdxRegexString}`;
suffixRegex = caseSensitive ? new RegExp(suffixRegexString) : new RegExp(suffixRegexString, "i");
if (!suffixRegex.test(value) && !valueIdxRegex.test(value)) {
if (isSuffixOptional && !propertyValueExists({
caseSensitive,
items,
propertyPath,
propertyPathAlternate,
value
})) {
return value;
}
value += `${separator}${suffix}`;
}
}
let valueIdx = 1;
while (propertyValueExists({
caseSensitive,
items,
propertyPath,
propertyPathAlternate,
value
})) {
if (valueIdx === 1) {
const existingValueMatch = value.match(valueIdxRegex);
if (existingValueMatch) {
const existingValueIdx = parseInt(
existingValueMatch[0].substring(separator.length),
10
);
if (identify1AsCounter) {
valueIdx = existingValueIdx;
} else {
const valueStem = value.replace(valueIdxRegex, "");
if (otherInstanceExists({
caseSensitive,
items,
propertyPath,
propertyPathAlternate,
separator,
value,
valueStem
})) {
valueIdx = existingValueIdx;
} else {
value += `${separator}2`;
}
}
}
if (suffix && suffixRegex && !suffixRegex.test(value)) {
value = value.replace(valueIdxRegex, `${separator}${suffix}$&`);
}
}
valueIdx++;
value = `${value.replace(valueIdxRegex, "")}${separator}${valueIdx}`;
}
return value;
};
export {
uniquify as default,
escapeRegExp
};
//# sourceMappingURL=uniquify.js.map