/** * orderColumn.ts * * Lightweight, strictly typed helper to compare two records by nested key path. * Exports: orderColumn, OrderColumnInput */ export type OrderColumnInput = { key: string [k: string]: any } /** * Safely resolves nested property by dot path. * @param obj object to read * @param path dot-separated path (e.g. 'a.b.c') */ const resolvePath = (obj: Record, path?: string): any => { if (!path) return undefined const keys = path.split('.').filter(Boolean) let cur: any = obj for (const k of keys) { if (cur == null) return undefined cur = cur[k] } return cur } /** * Compare two items for ordering based on currentColumn state. * * - If `first` is string -> localeCompare * - If number -> numeric compare * - Fallback -> 0 (stable) * * @param prev left item * @param post right item * @param currentColumn object like { key: 'field.path', 'field.path': 0 | 1 } * @returns negative | positive | 0 */ export const orderColumn = (prev: Record, post: Record, currentColumn: OrderColumnInput = { key: '' }): number => { const { key } = currentColumn if (!key) return 0 const firstElem = resolvePath(prev, key) const secondElem = resolvePath(post, key) // handle undefineds — move undefined to the end if (firstElem === undefined && secondElem === undefined) return 0 if (firstElem === undefined) return 1 if (secondElem === undefined) return -1 if (typeof firstElem === 'string' && typeof secondElem === 'string') { // currentColumn[key] === 1 => ascending? Keep existing semantics: if (currentColumn[key] === 1) { // 1: numeric compare? preserving previous behaviour: second - first return secondElem.localeCompare(firstElem) } if (currentColumn[key] === 0) { return firstElem.localeCompare(secondElem) } return firstElem.localeCompare(secondElem) } if (typeof firstElem === 'number' && typeof secondElem === 'number') { if (currentColumn[key] === 1) { return firstElem - secondElem } if (currentColumn[key] === 0) { return secondElem - firstElem } return firstElem - secondElem } // fallback to string conversion const a = String(firstElem) const b = String(secondElem) return a.localeCompare(b) } /* --------------------------- Basic test examples (plain TS) --------------------------- These are tiny, dependency-free tests you can run manually or port to your test runner. They are intentionally minimal and use runtime asserts. */ if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'test') { const assert = (condition: boolean, msg?: string) => { if (!condition) throw new Error(msg || 'Assertion failed') } // test: numeric ascending ;(() => { const a = { v: 1 }, b = { v: 2 } const res = orderColumn(a, b, { key: 'v', 'v': 1 }) assert(res < 0, 'expected 1 < 2 with key v asc') })() // test: string descending ;(() => { const a = { name: 'a' }, b = { name: 'b' } const res = orderColumn(a, b, { key: 'name', name: 0 }) assert(res < 0, 'expected a < b with name desc (locale)') })() }