UNPKG

nhb-toolbox

Version:

A versatile collection of smart, efficient, and reusable utility functions, classes and types for everyday development needs.

102 lines (101 loc) 3.93 kB
import { isArrayOfType, isObject, isValidArray } from '../guards/non-primitives.js'; import { isBoolean, isNumber, isString } from '../guards/primitives.js'; export function naturalSort(a, b, options) { const { caseInsensitive = true, localeAware = false } = options || {}; const _createChunks = (str) => { const chunks = []; let current = ''; let isNumeric = false; for (const char of str) { const charIsNum = !Number.isNaN(Number(char)); if (current?.length === 0) { current = char; isNumeric = charIsNum; continue; } if (charIsNum === isNumeric) { current += char; } else { chunks?.push(isNumeric ? Number(current) : current); current = char; isNumeric = charIsNum; } } if (current?.length > 0) { chunks?.push(isNumeric ? Number(current) : current); } return chunks; }; const aChunks = _createChunks(a); const bChunks = _createChunks(b); for (let i = 0; i < Math.min(aChunks?.length, bChunks?.length); i++) { let aChunk = aChunks[i]; let bChunk = bChunks[i]; if (caseInsensitive && isString(aChunk) && isString(bChunk)) { aChunk = aChunk?.toLowerCase(); bChunk = bChunk?.toLowerCase(); } if (typeof aChunk !== typeof bChunk) { return isString(aChunk) ? 1 : -1; } if (aChunk !== bChunk) { if (isNumber(aChunk) && isNumber(bChunk)) { return aChunk - bChunk; } if (isString(aChunk) && isString(bChunk)) { if (localeAware) { const cmp = aChunk.localeCompare(bChunk, undefined, { sensitivity: caseInsensitive ? 'accent' : 'variant', }); if (cmp !== 0) return cmp; } return aChunk < bChunk ? -1 : 1; } } } return aChunks?.length - bChunks?.length; } export function sortAnArray(array, options) { if (!isValidArray(array)) return array; if (isArrayOfType(array, isString)) { return [...array].sort((a, b) => options?.sortOrder === 'desc' ? naturalSort(b, a) : naturalSort(a, b)); } if (isArrayOfType(array, isNumber)) { return [...array].sort((a, b) => (options?.sortOrder === 'desc' ? b - a : a - b)); } if (isArrayOfType(array, isBoolean)) { return [...array].sort((a, b) => options?.sortOrder === 'desc' ? Number(b) - Number(a) : Number(a) - Number(b)); } if (isArrayOfType(array, isObject) && options && 'sortByField' in options) { return [...array].sort((a, b) => { const _getKeyValue = (obj, path) => { return path .split('.') .reduce((acc, key) => acc?.[key], obj); }; const keyA = _getKeyValue(a, options?.sortByField); const keyB = _getKeyValue(b, options?.sortByField); if (keyA == null || keyB == null) { return keyA == null ? 1 : -1; } if (isString(keyA) && isString(keyB)) { return options?.sortOrder === 'desc' ? naturalSort(keyB, keyA) : naturalSort(keyA, keyB); } if (isNumber(keyA) && isNumber(keyB)) { return options?.sortOrder === 'desc' ? keyB - keyA : keyA - keyB; } if (isBoolean(keyA) && isBoolean(keyB)) { return options?.sortOrder === 'desc' ? Number(keyB) - Number(keyA) : Number(keyA) - Number(keyB); } return 0; }); } return [...array]; }