Itu adalah hari isolasi diri lainnya, dan saya melakukan salah satu proyek untuk diri saya sendiri yang kami tinggalkan beberapa hari setelah kami mulai. Anda tahu, proyek yang akan membuat Anda terkenal akan memungkinkan Anda mempelajari bahasa pemrograman baru, kerangka kerja baru, dan semua itu. Secara umum, itu adalah hari yang paling umum, pandemi paling umum. Tidak ada yang meramalkan masalah, sampai satu perpustakaan yang saya gunakan untuk bekerja dengan array jatuh dengan tumpukan melimpah ... Dan saat itulah semuanya mulai menggelembung.
Secara umum, saya sangat mengerti bahwa itu mungkin untuk menggunakan sesuatu yang sudah jadi, tetapi tidak begitu menarik.
Mengapa LINQ?
Secara singkat bagi mereka yang belum tahu:
LINQ (Language-Integrated Query) adalah bahasa yang sederhana dan nyaman untuk menanyakan sumber data. Objek yang mengimplementasikan antarmuka IEnumerable (misalnya, koleksi standar, array), DataSet, dokumen XML dapat bertindak sebagai sumber data.
Dan LINQ memungkinkan Anda melakukan ini:
string[] teams = { "", "", " ", " ", "", "" };
var selectedTeams = teams.Where(t=>t.ToUpper().StartsWith("")).OrderBy(t => t);
Dari keuntungan khusus, saya ingin mencatat:
- Perhitungan malas
- Mengoptimalkan kueri
- Sistem metode penyuluhan yang nyaman
Saya tidak tahu tentang Anda, tetapi bagi saya pribadi itu berfungsi sebagai argumen yang menarik untuk mulai bekerja.
Mulai
Saya ingin segera, dengan pedang botak, bergegas ke bisnis, tetapi kami tidak akan melakukannya - kami umumnya orang-orang serius yang menulis hal-hal serius.
Oleh karena itu, kami akan menetapkan beberapa persyaratan untuk diri kami sendiri, selain yang diindikasikan dalam keunggulan LINQ:
- Kode harus dapat diperluas dengan mudah
- Kode harus cepat
Benchmark.js. Lodash, , .
, , , , npm-.
, , , .
:
- Where
- Sort
- Min
, -, :
- Where , .
- Sort , .
- Min .

:

, :

, , , .
export class Collection<T> implements ICollection<T> {
protected inner: Collection<T>;
private _computed: T[] | null = null; // , ""
public where(condition: FilterCondition<T>): ICollection<T> {
return new FilteringCollection<T>(this, condition);
}
public sort(condition?: CompareCondition<T> | undefined): ICollection<T> {
return new SortingCollection<T>(this, {
compare: condition
})
}
public min(predicate?: CompareCondition<T> | undefined): T {
return new MinAggregator(this, predicate).aggregate();
}
public toArray(): T[] {
return this.computed;
}
public [Symbol.iterator](): IterableIterator<T> {
return this.getIterator();
}
public getIterator(): IterableIterator<T> {
return this.computed[Symbol.iterator](); // , for - of
}
private get computed(): T[] { // :
if (this._computed == null) {
const result = this.materialize();
Object.freeze(result);
this._computed = result;
}
return this._computed
}
protected materialize(): T[] { //
return this.inner.toArray();
}
}
" ?" — , : " ".
:
const collection = new Collection([6, 5, 4, 3, 2, 1]);
const result = collection.where(item => item % 2).sort(); // [1, 3, 5]
, :
const collection = new Collection([6, 5, 4, 3, 2, 1]);
/* 1) */const filtered = collection.where(item => item % 2);
/* 2) */const sorted = filtered.sort();
1) where Collection, inner.
2) sort FilteringCollection, inner.
, , :
1) materialize FilteringCollection [5, 3, 1].
2) materialize SortingCollection [1, 3, 5].
Where
export class FilteringCollection<T> extends Collection<T> {
public constructor(iterable: Collection<T> | T[], private condition: FilterCondition<T>) {
super(iterable);
}
public where(condition: FilterCondition<T>): ICollection<T> { // where
const result = new FilteringCollection<T>(this.inner, item => condition(item) && that.condition(item));
return result;
}
protected materialize(): T[] { //
return this.inner.toArray().filter(this.condition);
}
}
. .
, where(). , ?
, where:
_(cats).where(cat => cat.age < 3).where(cat => cat.age > 1).toArray()
_(cats).where(cat => cat.age < 3 && (function(item){
return item.age > 1;
}(cat))).toArray()
:
public where(condition: FilterCondition<T>): ICollection<T> { // where
const result = new FilteringCollection<T>(this.inner, item => condition(item) && this.condition(item)); // <--
return result;
}
"".
materialize filter. . , .
----------------------------------------------------
Filter for 1000000:
Where x 104 ops/sec ±14.73% (61 runs sampled)
Lodash filter x 609 ops/sec ±0.67% (88 runs sampled)
Native filter x 537 ops/sec ±1.69% (85 runs sampled)
Double where x 102 ops/sec ±11.51% (64 runs sampled)
Double lodash filter x 368 ops/sec ±1.00% (88 runs sampled)
Double native filter x 336 ops/sec ±1.08% (84 runs sampled)
10 where x 66.60 ops/sec ±9.15% (59 runs sampled)
10 lodash filter x 99.44 ops/sec ±1.20% (73 runs sampled)
10 native filter x 81.80 ops/sec ±1.33% (70 runs sampled)
----------------------------------------------------
Filter for 1000:
Where x 24,296 ops/sec ±0.90% (88 runs sampled)
Lodash filter x 60,927 ops/sec ±0.90% (89 runs sampled)
Native filter x 204,522 ops/sec ±6.76% (87 runs sampled)
Double where x 20,281 ops/sec ±0.86% (90 runs sampled)
Double lodash filter x 37,553 ops/sec ±0.97% (90 runs sampled)
Double native filter x 115,652 ops/sec ±6.12% (91 runs sampled)
10 where x 9,559 ops/sec ±1.09% (87 runs sampled)
10 lodash filter x 8,850 ops/sec ±0.80% (87 runs sampled)
10 native filter x 22,507 ops/sec ±9.22% (84 runs sampled)
----------------------------------------------------
Filter for 10:
Where x 1,788,009 ops/sec ±0.81% (87 runs sampled)
Lodash filter x 720,558 ops/sec ±0.80% (84 runs sampled)
Native filter x 14,917,151 ops/sec ±0.61% (85 runs sampled)
Double where x 1,257,163 ops/sec ±0.52% (95 runs sampled)
Double lodash filter x 456,365 ops/sec ±0.74% (76 runs sampled)
Double native filter x 8,262,940 ops/sec ±0.64% (90 runs sampled)
10 where x 489,733 ops/sec ±0.67% (94 runs sampled)
10 lodash filter x 135,275 ops/sec ±0.61% (94 runs sampled)
10 native filter x 1,350,316 ops/sec ±0.94% (90 runs sampled)
---------------------------------------------------- : , .
select ( ).
Sort
export class SortingCollection<T, V = T> extends Collection<T> implements ISortingCollection<T> {
private sortSettings: SortSettings<T, V>[];
public constructor(iterable: Collection<T>, ...sortSettings: SortSettings<T, V>[]) {
super(iterable);
this.sortSettings = _(sortSettings)
.where(item => !!item.compare || !!item.mapping) //
.toArray();
}
protected materialize(): T[] {
const comparer = new Comparer(this.sortSettings, this.defaultCompare);
return Array.from(this.inner.toArray()).sort(this.sortSettings.length ? (first, second) => comparer.compare(first, second) : undefined);
}
private defaultCompare(first: V, second: V): number {
if(first < second) {
return -1
} else if (second < first) {
return 1
} else {
return 0;
}
}
}
. . Comparer.
Lodash, js .

----------------------------------------------------
Sort for 1000000:
Sort x 0.80 ops/sec ±3.59% (6 runs sampled)
Lodash sort x 0.97 ops/sec ±27.98% (7 runs sampled)
Native sort x 1.05 ops/sec ±14.71% (7 runs sampled)
SortBy x 0.19 ops/sec ±10.31% (5 runs sampled)
Lodash SortBy x 0.37 ops/sec ±7.21% (5 runs sampled)
Sort after map x 0.47 ops/sec ±8.67% (6 runs sampled)
----------------------------------------------------
Sort for 1000:
Sort x 1,121 ops/sec ±0.77% (85 runs sampled)
Lodash sort x 1,267 ops/sec ±0.77% (89 runs sampled)
Native sort x 1,274 ops/sec ±0.88% (86 runs sampled)
SortBy x 488 ops/sec ±1.45% (80 runs sampled)
Lodash SortBy x 549 ops/sec ±9.60% (70 runs sampled)
Sort after map x 954 ops/sec ±1.50% (83 runs sampled)
----------------------------------------------------
Sort for 10:
Sort x 171,700 ops/sec ±1.38% (85 runs sampled)
Lodash sort x 196,364 ops/sec ±2.01% (80 runs sampled)
Native sort x 250,820 ops/sec ±0.96% (85 runs sampled)
SortBy x 114,064 ops/sec ±0.90% (86 runs sampled)
Lodash SortBy x 86,370 ops/sec ±17.93% (67 runs sampled)
Sort after map x 221,034 ops/sec ±1.31% (87 runs sampled)
---------------------------------------------------- :
± .
Min
Menerapkan agregator pencarian elemen minimum
export class MinAggregator<T> extends ReduceAggregator<T> {
public constructor(collection: ICollection<T>, condition?: CompareCondition<T>) {
super(collection, condition ? (first, second) => this.compare(first, second, condition) : (a, b) => a < b ? a : b)
}
private compare(first: T, second: T, func: CompareCondition<T>): T {
return func(first, second) < 0 ? first : second;
}
}
export class ReduceAggregator<T> extends Aggregator<T> {
public constructor(private collection: ICollection<T>, protected predicate: ReduceCondition<T>){
super();
}
public aggregate(): T {
return this.collection.toArray().reduce((first, second) => this.predicate(first, second))
}
}
Mereka tidak menyangka? Dan tidak akan ada dekorator.
Sebagai gantinya, kami akan memiliki agregator.
Penjelasan
Saya tidak yakin apakah kami membutuhkannya sama sekali, kami hanya melakukan konvolusi.
----------------------------------------------------
Aggregate for 1000000:
Min x 43.69 ops/sec ±28.48% (65 runs sampled)
Lodash Min x 117 ops/sec ±0.58% (73 runs sampled)
----------------------------------------------------
Aggregate for 1000:
Min x 61,220 ops/sec ±5.10% (87 runs sampled)
Lodash Min x 111,452 ops/sec ±0.72% (90 runs sampled)
----------------------------------------------------
Aggregate for 10:
Min x 3,502,264 ops/sec ±1.36% (86 runs sampled)
Lodash Min x 4,181,189 ops/sec ±1.48% (86 runs sampled)
----------------------------------------------------
Aggregate By for 1000000 disp 50:
Min By x 23.81 ops/sec ±2.02% (42 runs sampled)
Lodash Min By x 42.66 ops/sec ±2.46% (55 runs sampled)
----------------------------------------------------
Aggregate By for 1000 disp 50:
Min By x 43,064 ops/sec ±0.71% (86 runs sampled)
Lodash Min By x 60,212 ops/sec ±0.89% (87 runs sampled)
----------------------------------------------------
Aggregate By for 100 disp 50:
Min By x 351,098 ops/sec ±1.03% (81 runs sampled)
Lodash Min By x 382,302 ops/sec ±1.39% (76 runs sampled)
----------------------------------------------------Lodash menang.
Hasil
Saya bersenang-senang dan waktu yang menarik.
Menurut saya, perpustakaan itu bagus dan ingin saya kembangkan.
Jika ada yang tahu cara mengurangi biaya iterasi untuk pemfilteran dan pemetaan, yaitu di sini:
this.inner.toArray().filter(this.condition);
Saya akan mencari tahu jawabannya!
Saya terbuka untuk kritik, saya ingin membuat kode lebih baik.
Terima kasih atas perhatiannya.
→ Repositori
→ Paket Npm