UNPKG

mingo

Version:

MongoDB query language for in-memory objects

78 lines (77 loc) 2.77 kB
import { assert, compare, groupBy, isEmpty, isNumber, isObject, isString, resolve } from "../../util"; const $sort = (collection, sortKeys, options) => { if (isEmpty(sortKeys) || !isObject(sortKeys)) return collection; let cmp = compare; const collationSpec = options.collation; if (isObject(collationSpec) && isString(collationSpec.locale)) { cmp = collationComparator(collationSpec); } return collection.transform((coll) => { const modifiers = Object.keys(sortKeys); for (const key of modifiers.reverse()) { const groups = groupBy(coll, (obj) => resolve(obj, key)); const sortedKeys = Array.from(groups.keys()); let nativeSorted = false; if (cmp === compare) { let t_str = true; let t_num = true; nativeSorted = sortedKeys.every( (v) => +(t_str &&= isString(v)) ^ +(t_num &&= isNumber(v)) ); if (t_str) sortedKeys.sort(); else if (t_num) { new Float64Array(sortedKeys).sort().forEach((v, i2) => sortedKeys[i2] = v); } } if (!nativeSorted) sortedKeys.sort(cmp); if (sortKeys[key] === -1) sortedKeys.reverse(); let i = 0; for (const k of sortedKeys) for (const v of groups.get(k)) coll[i++] = v; assert(i == coll.length, "bug: counter must match collection size."); } return coll; }); }; const COLLATION_STRENGTH = { // Only strings that differ in base letters compare as unequal. Examples: a ≠ b, a = á, a = A. 1: "base", // Only strings that differ in base letters or accents and other diacritic marks compare as unequal. // Examples: a ≠ b, a ≠ á, a = A. 2: "accent", // Strings that differ in base letters, accents and other diacritic marks, or case compare as unequal. // Other differences may also be taken into consideration. Examples: a ≠ b, a ≠ á, a ≠ A 3: "variant" // case - Only strings that differ in base letters or case compare as unequal. Examples: a ≠ b, a = á, a ≠ A. }; function collationComparator(spec) { const localeOpt = { sensitivity: COLLATION_STRENGTH[spec.strength || 3], caseFirst: spec.caseFirst === "off" ? "false" : spec.caseFirst || "false", numeric: spec.numericOrdering || false, ignorePunctuation: spec.alternate === "shifted" }; if (spec.caseLevel === true) { if (localeOpt.sensitivity === "base") localeOpt.sensitivity = "case"; if (localeOpt.sensitivity === "accent") localeOpt.sensitivity = "variant"; } const collator = new Intl.Collator(spec.locale, localeOpt); return (a, b) => { if (!isString(a) || !isString(b)) return compare(a, b); const i = collator.compare(a, b); if (i < 0) return -1; if (i > 0) return 1; return 0; }; } export { $sort };