Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 0 additions & 27 deletions src/Collection.js

This file was deleted.

149 changes: 149 additions & 0 deletions src/Collection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { ITERATE_ENTRIES, type IteratorType } from './Iterator';
import { IndexedSeq, KeyedSeq, Seq, SetSeq } from './Seq';
import type ValueObject from './ValueObject';
import { isAssociative } from './predicates/isAssociative';
import { isCollection } from './predicates/isCollection';
import { isIndexed } from './predicates/isIndexed';
import { isKeyed } from './predicates/isKeyed';
import assertNotInfinite from './utils/assertNotInfinite';
import deepEqual from './utils/deepEqual';
import { hashCollection } from './utils/hashCollection';

export function Collection<I extends CollectionImpl<unknown, unknown>>(
collection: I
): I;
export function Collection<T>(
collection: Iterable<T> | ArrayLike<T>
): IndexedCollectionImpl<T>;
export function Collection<V>(obj: {
[key: string]: V;
}): KeyedCollectionImpl<string, V>;
export function Collection<K = unknown, V = unknown>(
value: never
): CollectionImpl<K, V>;
export function Collection(value: unknown): CollectionImpl<unknown, unknown> {
return isCollection(value) ? value : Seq(value);
}

export class CollectionImpl<K, V> implements ValueObject {
private __hash: number | undefined;

size: number = 0;

equals(other: unknown): boolean {
return deepEqual(this, other);
}

hashCode() {
return this.__hash || (this.__hash = hashCollection(this));
}

every(
predicate: (value: V, key: K, iter: this) => boolean,
context?: CollectionImpl<K, V>
): boolean {
assertNotInfinite(this.size);
let returnValue = true;
this.__iterate((v, k, c) => {
if (!predicate.call(context, v, k, c)) {
returnValue = false;
return false;
}
});
return returnValue;
}

entries() {
return this.__iterator(ITERATE_ENTRIES);
}

__iterate(
fn: (value: V, index: K, iter: this) => boolean,
reverse?: boolean
): number;
__iterate(
fn: (value: V, index: K, iter: this) => void,
reverse?: boolean
): void;
__iterate(
fn: (value: V, index: K, iter: this) => boolean | void,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
reverse: boolean = false
): number | void {
throw new Error(
'CollectionImpl does not implement __iterate. Use a subclass instead.'
);
}

__iterator(
type: IteratorType,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
reverse: boolean = false
): Iterator<K | V | [K, V]> {
throw new Error(
'CollectionImpl does not implement __iterator. Use a subclass instead.'
);
}
}

/**
* Always returns a Seq.Keyed, if input is not keyed, expects an
* collection of [K, V] tuples.
*
* Note: `Seq.Keyed` is a conversion function and not a class, and does not
* use the `new` keyword during construction.
*/
export function KeyedCollection<K, V>(
collection?: Iterable<[K, V]>
): KeyedCollectionImpl<K, V>;
export function KeyedCollection<V>(obj: {
[key: string]: V;
}): KeyedCollectionImpl<string, V>;
export function KeyedCollection(
value: unknown
): KeyedCollectionImpl<unknown, unknown> {
return isKeyed(value) ? value : KeyedSeq(value);
}

export class KeyedCollectionImpl<K, V> extends CollectionImpl<K, V> {}

export function IndexedCollection<T>(
value: Iterable<T> | ArrayLike<T>
): IndexedCollectionImpl<T> {
return isIndexed(value) ? value : IndexedSeq(value);
}

/**
* Interface representing all oredered collections.
* This includes `List`, `Stack`, `Map`, `OrderedMap`, `Set`, and `OrderedSet`.
* return of `isOrdered()` return true in that case.
*/
interface OrderedCollection<T> {
/**
* Shallowly converts this collection to an Array.
*/
toArray(): Array<T>;

[Symbol.iterator](): IterableIterator<T>;
}

export class IndexedCollectionImpl<T>
extends CollectionImpl<number, T>
implements OrderedCollection<T>
{
declare toArray: () => T[];

declare [Symbol.iterator]: () => IterableIterator<T>;
}

export function SetCollection<T>(
value: Iterable<T> | ArrayLike<T>
): SetCollectionImpl<T> {
return isCollection(value) && !isAssociative(value) ? value : SetSeq(value);
}

export class SetCollectionImpl<T> extends CollectionImpl<T, T> {}

Collection.Keyed = KeyedCollection;
Collection.Indexed = IndexedCollection;
Collection.Set = SetCollection;
84 changes: 8 additions & 76 deletions src/CollectionImpl.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,9 @@ import {
KeyedCollectionImpl,
SetCollectionImpl,
} from './Collection';
import { hash } from './Hash';
import {
ITERATE_ENTRIES,
ITERATE_KEYS,
ITERATE_VALUES,
Iterator,
} from './Iterator';
import { ITERATE_KEYS, ITERATE_VALUES, Iterator } from './Iterator';
import { List } from './List';
import { Map } from './Map';
import { imul, smi } from './Math';
import {
concatFactory,
countByFactory,
Expand Down Expand Up @@ -65,10 +58,9 @@ import { toObject } from './methods/toObject';
import { IS_COLLECTION_SYMBOL } from './predicates/isCollection';
import { IS_INDEXED_SYMBOL, isIndexed } from './predicates/isIndexed';
import { IS_KEYED_SYMBOL, isKeyed } from './predicates/isKeyed';
import { IS_ORDERED_SYMBOL, isOrdered } from './predicates/isOrdered';
import { IS_ORDERED_SYMBOL } from './predicates/isOrdered';
import { toJS } from './toJS';
import assertNotInfinite from './utils/assertNotInfinite';
import deepEqual from './utils/deepEqual';
import mixin from './utils/mixin';
import quoteString from './utils/quoteString';

Expand Down Expand Up @@ -176,22 +168,6 @@ mixin(CollectionImpl, {
return this.some((value) => is(value, searchValue));
},

entries() {
return this.__iterator(ITERATE_ENTRIES);
},

every(predicate, context) {
assertNotInfinite(this.size);
let returnValue = true;
this.__iterate((v, k, c) => {
if (!predicate.call(context, v, k, c)) {
returnValue = false;
return false;
}
});
return returnValue;
},

filter(predicate, context) {
return reify(this, filterFactory(this, predicate, context, true));
},
Expand Down Expand Up @@ -301,9 +277,9 @@ mixin(CollectionImpl, {
return countByFactory(this, grouper, context);
},

equals(other) {
return deepEqual(this, other);
},
// equals(other) {
// return deepEqual(this, other);
// },

entrySeq() {
// eslint-disable-next-line @typescript-eslint/no-this-alias
Expand Down Expand Up @@ -482,9 +458,9 @@ mixin(CollectionImpl, {

// ### Hashable Object

hashCode() {
return this.__hash || (this.__hash = hashCollection(this));
},
// hashCode() {
// return this.__hash || (this.__hash = hashCollection(this));
// },

// ### Internal

Expand Down Expand Up @@ -755,47 +731,3 @@ function defaultZipper(...values) {
function defaultNegComparator(a, b) {
return a < b ? 1 : a > b ? -1 : 0;
}

function hashCollection(collection) {
if (collection.size === Infinity) {
return 0;
}
const ordered = isOrdered(collection);
const keyed = isKeyed(collection);
let h = ordered ? 1 : 0;

collection.__iterate(
keyed
? ordered
? (v, k) => {
h = (31 * h + hashMerge(hash(v), hash(k))) | 0;
}
: (v, k) => {
h = (h + hashMerge(hash(v), hash(k))) | 0;
}
: ordered
? (v) => {
h = (31 * h + hash(v)) | 0;
}
: (v) => {
h = (h + hash(v)) | 0;
}
);

return murmurHashOfSize(collection.size, h);
}

function murmurHashOfSize(size, h) {
h = imul(h, 0xcc9e2d51);
h = imul((h << 15) | (h >>> -15), 0x1b873593);
h = imul((h << 13) | (h >>> -13), 5);
h = ((h + 0xe6546b64) | 0) ^ size;
h = imul(h ^ (h >>> 16), 0x85ebca6b);
h = imul(h ^ (h >>> 13), 0xc2b2ae35);
h = smi(h ^ (h >>> 16));
return h;
}

function hashMerge(a, b) {
return (a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2))) | 0; // int
}
Loading