diff --git a/@sammo/util/src/index.ts b/@sammo/util/src/index.ts index 2c2c730..2cdef73 100644 --- a/@sammo/util/src/index.ts +++ b/@sammo/util/src/index.ts @@ -6,6 +6,7 @@ export * from "./must.js" export * from "./types.js" export * from "./web.js" export * from "./signal.js" +export * from "./sorted.js" export * from "./strongType.js" diff --git a/@sammo/util/src/sorted.ts b/@sammo/util/src/sorted.ts new file mode 100644 index 0000000..2af433a --- /dev/null +++ b/@sammo/util/src/sorted.ts @@ -0,0 +1,75 @@ +type IndexableRaw = { + [key: number]: T; +} +type IndexableGet = { + get(index: number): T | undefined; +} + +type IndexableAt = { + at(index: number): T; +} + +interface BasicResizableArrayLike { + length: number; + splice(start: number, deleteCount: number, ...items: T[]): T[] | undefined; + shift(): T | undefined; + unshift(items: T): number; + push(items: T): number; + pop(): T | undefined; +} + +export type ResizableArrayLike = BasicResizableArrayLike & (IndexableRaw | IndexableGet | IndexableAt); +/** + * 이미 정렬된 배열에 아이템 추가. + * Array와 Denque 지원. + * + * @param arr 정렬된 배열, splice, shift, unshift, push, pop과 at, get, []중 하나의 조회법 지원 + * @param item 추가할 아이템 + * @param compareFn 비교함수. 앞에 오는 경우 -1, 뒤에 오는 경우 1, 같은 경우 0을 반환해야 함. + * @param maybeTail false인 경우 binary search, true인 경우 뒤부터 순차 검색 + * + * @returns + */ +export function insertItemAtArrayLike>(arr: Arr, item: T, compareFn: (a: T, b: T) => number, maybeTail = false): ResizableArrayLike { + const getter: ((i: number) => T) = (() => { + if ('at' in arr) { + return (i: number) => arr.at(i)!; + } + if ('get' in arr) { + return (i: number) => arr.get(i)!; + } + return (i: number) => arr[i]; + })(); + + if (arr.length === 0) { + arr.push(item); + return arr; + } + + if (maybeTail) { + for (let i = arr.length - 1; i >= 0; i--) { + if (compareFn(getter(i), item) <= 0) { + arr.splice(i + 1, 0, item); + return arr; + } + } + //맨 앞이라고..? 대체 왜 maybeTail=true를 쓰는거지? + arr.unshift(item); + return arr; + } + + //binary search and insert + let left = 0; + let right = arr.length; + while (left < right) { + const mid = (left + right) >> 1; + const cmp = compareFn(getter(mid), item); + if (cmp < 0) { + left = mid + 1; + } else { + right = mid; + } + } + arr.splice(left, 0, item); + return arr; +} \ No newline at end of file