Skip to content

Commit

Permalink
Merge pull request #7 from sepiariver/bump-2.0.3
Browse files Browse the repository at this point in the history
Bump 2.0.3
  • Loading branch information
sepiariver authored Dec 8, 2024
2 parents d58138e + 03b2d4c commit 2333056
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 114 deletions.
96 changes: 46 additions & 50 deletions dist/index.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,48 @@
// index.ts
import equal from "fast-deep-equal/es6/index.js";
var serialize = (item) => {
if (typeof item === "number" && isNaN(item)) {
return "NaN";
}
if (item && typeof item === "object") {
if (Array.isArray(item)) {
return `[${item.map(serialize).join("")}]`;
} else {
return `{${Object.entries(item).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}:${serialize(v)}`).join("")}}`;
}
}
return String(item);
};
var fnv1a = (str) => {
if (typeof str !== "string") {
str = String(str);
}
let hash = 2166136261;
for (let i = 0; i < str.length; i++) {
hash ^= str.charCodeAt(i);
hash = hash * 16777619 >>> 0;
}
return hash >>> 0;
};
var findNextPrime = (num) => {
if (num < 2) return 2;
if ((num & 1) === 0) num++;
while (!isPrime(num)) {
num += 2;
}
return num;
};
var isPrime = (num) => {
if (num < 2) return false;
if (num === 2 || num === 3) return true;
if ((num & 1) === 0) return false;
if (num % 3 === 0) return false;
const sqrt = Math.sqrt(num);
for (let i = 5; i <= sqrt; i += 6) {
if (num % i === 0 || num % (i + 2) === 0) return false;
}
return true;
};
var UniqueSet = class extends Set {
/*** @throws TypeError If the input is not iterable. */
constructor(iterable = []) {
Expand Down Expand Up @@ -62,7 +105,7 @@ var BloomSet = class extends Set {
if (typeof size !== "number" || size <= 0) {
size = 6553577;
}
this.#aSize = this.#findNextPrime(size);
this.#aSize = findNextPrime(size);
if (typeof hashCount !== "number" || hashCount <= 0) {
hashCount = 7;
}
Expand All @@ -73,45 +116,10 @@ var BloomSet = class extends Set {
}
}
/** @internal */
#findNextPrime(num) {
if (num < 2) return 2;
if (num % 2 === 0) num++;
while (!this.#isPrime(num)) {
num += 2;
}
return num;
}
/** @internal */
#isPrime(num) {
if (num < 2) return false;
if (num === 2 || num === 3) return true;
if (num % 2 === 0 || num % 3 === 0) return false;
const sqrt = Math.floor(Math.sqrt(num));
for (let i = 5; i <= sqrt; i += 6) {
if (num % i === 0 || num % (i + 2) === 0) return false;
}
return true;
}
/** @internal */
#serialize(item) {
if (typeof item === "number" && isNaN(item)) {
return "NaN";
}
if (item && typeof item === "object") {
const serialize = this.#serialize.bind(this);
if (Array.isArray(item)) {
return `[${item.map(serialize).join(",")}]`;
} else {
return `{${Object.entries(item).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}:${serialize(v)}`).join(",")}}`;
}
}
return String(item);
}
/** @internal */
#hashes(item) {
const hashes = [];
const str = this.#serialize(item);
let hash = this.#fnv1a(str);
const str = serialize(item);
let hash = fnv1a(str);
for (let i = 0; i < this.#hashCount; i++) {
hash %= this.#aSize;
hashes.push(hash);
Expand All @@ -121,18 +129,6 @@ var BloomSet = class extends Set {
return hashes;
}
/** @internal */
#fnv1a(str) {
if (typeof str !== "string") {
str = String(str);
}
let hash = 2166136261;
for (let i = 0; i < str.length; i++) {
hash ^= str.charCodeAt(i);
hash = hash * 16777619 >>> 0;
}
return hash >>> 0;
}
/** @internal */
#setBits(hashes) {
for (const hash of hashes) {
const index = Math.floor(hash / 8);
Expand Down
124 changes: 61 additions & 63 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,63 @@
import equal from "fast-deep-equal/es6/index.js";

/** Utility functions */

const serialize = (item: any | number | object): string => {
if (typeof item === "number" && isNaN(item)) {
return "NaN";
}

if (item && typeof item === "object") {
if (Array.isArray(item)) {
return `[${item.map(serialize).join("")}]`;
} else {
return `{${Object.entries(item)
.sort(([a], [b]) => a.localeCompare(b))
.map(([k, v]) => `${k}:${serialize(v)}`)
.join("")}}`;
}
}

return String(item);
};

const fnv1a = (str: string) => {
if (typeof str !== "string") {
str = String(str);
}
let hash = 2166136261; // FNV offset basis for 32-bit
for (let i = 0; i < str.length; i++) {
hash ^= str.charCodeAt(i);
hash = (hash * 16777619) >>> 0; // Multiply by the FNV prime and ensure 32-bit unsigned
}
return hash >>> 0;
};

const findNextPrime = (num: number) => {
if (num < 2) return 2;
if ((num & 1) === 0) num++; // Odd numbers only

while (!isPrime(num)) {
num += 2; // Odd numbers only
}

return num;
};

const isPrime = (num: number): boolean => {
if (num < 2) return false;
if (num === 2 || num === 3) return true;
if ((num & 1) === 0) return false;
if (num % 3 === 0) return false;

const sqrt = Math.sqrt(num);
for (let i = 5; i <= sqrt; i += 6) {
if (num % i === 0 || num % (i + 2) === 0) return false;
}

return true;
};

/** A `Set` extension that ensures uniqueness of items using deep equality checks. */
export class UniqueSet<T> extends Set<T> {
/*** @throws TypeError If the input is not iterable. */
Expand Down Expand Up @@ -72,7 +130,7 @@ export class BloomSet<T> extends Set<T> {
if (typeof size !== "number" || size <= 0) {
size = 6553577; // Targeting < 1 collision per 100,000 elements, ~819 KB memory, needs 7 hashes
}
this.#aSize = this.#findNextPrime(size);
this.#aSize = findNextPrime(size);

if (typeof hashCount !== "number" || hashCount <= 0) {
hashCount = 7;
Expand All @@ -85,58 +143,11 @@ export class BloomSet<T> extends Set<T> {
}
}

/** @internal */
#findNextPrime(num: number) {
if (num < 2) return 2;
if (num % 2 === 0) num++; // Odd numbers only

while (!this.#isPrime(num)) {
num += 2; // Odd numbers only
}

return num;
}

/** @internal */
#isPrime(num: number) {
if (num < 2) return false;
if (num === 2 || num === 3) return true;
if (num % 2 === 0 || num % 3 === 0) return false;

const sqrt = Math.floor(Math.sqrt(num));
for (let i = 5; i <= sqrt; i += 6) {
if (num % i === 0 || num % (i + 2) === 0) return false;
}

return true;
}

/** @internal */
#serialize(item: T | number | object): string {
if (typeof item === "number" && isNaN(item)) {
return "NaN";
}

if (item && typeof item === "object") {
const serialize = this.#serialize.bind(this);
if (Array.isArray(item)) {
return `[${item.map(serialize).join(",")}]`;
} else {
return `{${Object.entries(item)
.sort(([a], [b]) => a.localeCompare(b))
.map(([k, v]) => `${k}:${serialize(v)}`)
.join(",")}}`;
}
}

return String(item);
}

/** @internal */
#hashes(item: T) {
const hashes: number[] = [];
const str = this.#serialize(item);
let hash = this.#fnv1a(str); // Base hash
const str = serialize(item);
let hash = fnv1a(str); // Base hash

// Bloom into hashCount hash values
for (let i = 0; i < this.#hashCount; i++) {
Expand All @@ -151,19 +162,6 @@ export class BloomSet<T> extends Set<T> {
return hashes;
}

/** @internal */
#fnv1a(str: string) {
if (typeof str !== "string") {
str = String(str);
}
let hash = 2166136261; // FNV offset basis for 32-bit
for (let i = 0; i < str.length; i++) {
hash ^= str.charCodeAt(i);
hash = (hash * 16777619) >>> 0; // Multiply by the FNV prime and ensure 32-bit unsigned
}
return hash >>> 0;
}

/** @internal */
#setBits(hashes: number[]): void {
for (const hash of hashes) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sepiariver/unique-set",
"version": "2.0.2",
"version": "2.0.3",
"description": "Extends the native Set class to deeply compare using fast-deep-equal, with optional Bloom filter optimization. This version exports 2 classes instead of a default, breaking b/c with version 1.",
"main": "dist/index.js",
"module": "dist/index.mjs",
Expand Down

0 comments on commit 2333056

Please sign in to comment.