From 1ebe098805ffa47147044563043c157746bfbe5f Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Sun, 17 Dec 2023 18:06:58 -0300 Subject: [PATCH] binarySearch and improve insertEventInto___List() to use that and .splice() --- utils.test.ts | 16 ++++++--- utils.ts | 99 +++++++++++++++++---------------------------------- 2 files changed, 45 insertions(+), 70 deletions(-) diff --git a/utils.test.ts b/utils.test.ts index 4a3d141..9f77be8 100644 --- a/utils.test.ts +++ b/utils.test.ts @@ -1,6 +1,6 @@ import { describe, test, expect } from 'bun:test' import { buildEvent } from './test-helpers.ts' -import { Queue, insertEventIntoAscendingList, insertEventIntoDescendingList } from './utils.ts' +import { Queue, insertEventIntoAscendingList, insertEventIntoDescendingList, binarySearch } from './utils.ts' import type { Event } from './event.ts' @@ -214,13 +214,13 @@ describe('inserting into a asc sorted list of events', () => { }) }) -describe('enque a message into MessageQueue', () => { - test('enque into an empty queue', () => { +describe('enqueue a message into MessageQueue', () => { + test('enqueue into an empty queue', () => { const queue = new Queue() queue.enqueue('node1') expect(queue.first!.value).toBe('node1') }) - test('enque into a non-empty queue', () => { + test('enqueue into a non-empty queue', () => { const queue = new Queue() queue.enqueue('node1') queue.enqueue('node3') @@ -255,3 +255,11 @@ describe('enque a message into MessageQueue', () => { expect(item3).toBe(null) }) }) + +test('binary search', () => { + expect(binarySearch(['a', 'b', 'd', 'e'], 'e', (a, b) => (a < b ? -1 : a === b ? 0 : 1))).toEqual([3, true]) + expect(binarySearch(['a', 'b', 'd', 'e'], 'x', (a, b) => (a < b ? -1 : a === b ? 0 : 1))).toEqual([4, false]) + expect(binarySearch(['a', 'b', 'd', 'e'], 'c', (a, b) => (a < b ? -1 : a === b ? 0 : 1))).toEqual([2, false]) + expect(binarySearch(['a', 'b', 'd', 'e'], 'a', (a, b) => (a < b ? -1 : a === b ? 0 : 1))).toEqual([0, true]) + expect(binarySearch(['a', 'b', 'd', 'e'], '[', (a, b) => (a < b ? -1 : a === b ? 0 : 1))).toEqual([0, false]) +}) diff --git a/utils.ts b/utils.ts index ab6ddd4..85cef37 100644 --- a/utils.ts +++ b/utils.ts @@ -13,83 +13,50 @@ export function normalizeURL(url: string): string { return p.toString() } -// -// fast insert-into-sorted-array functions adapted from https://github.com/terrymorse58/fast-sorted-array -// export function insertEventIntoDescendingList(sortedArray: Event[], event: Event) { - let start = 0 - let end = sortedArray.length - 1 - let midPoint - let position = start - - if (end < 0) { - position = 0 - } else if (event.created_at < sortedArray[end].created_at) { - position = end + 1 - } else if (event.created_at >= sortedArray[start].created_at) { - position = start - } else - while (true) { - if (end <= start + 1) { - position = end - break - } - midPoint = Math.floor(start + (end - start) / 2) - if (sortedArray[midPoint].created_at > event.created_at) { - start = midPoint - } else if (sortedArray[midPoint].created_at < event.created_at) { - end = midPoint - } else { - // aMidPoint === num - position = midPoint - break - } - } - - // insert when num is NOT already in (no duplicates) - if (sortedArray[position]?.id !== event.id) { - return [...sortedArray.slice(0, position), event, ...sortedArray.slice(position)] + const [idx, found] = binarySearch(sortedArray, event, (a, b) => { + if (a.id === b.id) return 0 + if (a.created_at === b.created_at) return -1 + return b.created_at - a.created_at + }) + if (!found) { + sortedArray.splice(idx, 0, event) } - return sortedArray } export function insertEventIntoAscendingList(sortedArray: Event[], event: Event) { - let start = 0 - let end = sortedArray.length - 1 - let midPoint - let position = start + const [idx, found] = binarySearch(sortedArray, event, (a, b) => { + if (a.id === b.id) return 0 + if (a.created_at === b.created_at) return -1 + return a.created_at - b.created_at + }) + if (!found) { + sortedArray.splice(idx, 0, event) + } + return sortedArray +} - if (end < 0) { - position = 0 - } else if (event.created_at > sortedArray[end].created_at) { - position = end + 1 - } else if (event.created_at <= sortedArray[start].created_at) { - position = start - } else - while (true) { - if (end <= start + 1) { - position = end - break - } - midPoint = Math.floor(start + (end - start) / 2) - if (sortedArray[midPoint].created_at < event.created_at) { - start = midPoint - } else if (sortedArray[midPoint].created_at > event.created_at) { - end = midPoint - } else { - // aMidPoint === num - position = midPoint - break - } +export function binarySearch(arr: T[], val: T, compare: (a: T, b: T) => number): [number, boolean] { + let start = 0 + let end = arr.length - 1 + + while (start <= end) { + const mid = Math.floor((start + end) / 2) + const cmp = compare(val, arr[mid]) + + if (cmp === 0) { + return [mid, true] } - // insert when num is NOT already in (no duplicates) - if (sortedArray[position]?.id !== event.id) { - return [...sortedArray.slice(0, position), event, ...sortedArray.slice(position)] + if (cmp < 0) { + end = mid - 1 + } else { + start = mid + 1 + } } - return sortedArray + return [start, false] } export class QueueNode {