Skip to content

Commit

Permalink
1. Add repeat generator
Browse files Browse the repository at this point in the history
2. Add distinct benchmark
3. Remove asserts from benchmarks.
4. Add binary search and insert in ordered place utils methods.
  • Loading branch information
Indomitable committed Aug 12, 2019
1 parent c8643f7 commit 21f414a
Show file tree
Hide file tree
Showing 16 changed files with 283 additions and 54 deletions.
8 changes: 8 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,4 +308,12 @@ declare module 'modern-linq' {
* @param value
*/
export function from<TValue extends {}, TKey extends keyof TValue>(value: TValue): LinqIterable<{ key: string, value: TValue[TKey] }>;

/**
* Creates a select js iterable containing a [from, to) range of numbers
* if from is less than to return ascending range
* if from is greater that to return descending range
* if from === to returns empty iterable
*/
export function repeat<TValue>(value: TValue, times: number): LinqIterable<TValue>;
}
2 changes: 1 addition & 1 deletion index.esm.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { fromIterable, fromObject, fromArrayLike, range, from } from './src/index';
export { fromIterable, fromObject, fromArrayLike, range, from, repeat } from './src/index';
5 changes: 5 additions & 0 deletions src/creation.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { RangeIterable } from "./generators/range";
import { BaseLinqIterable } from "./base-linq-iterable";
import { RepeatIterable } from "./generators/repeat";

export class LinqIterable extends BaseLinqIterable {
constructor(source) {
Expand Down Expand Up @@ -93,6 +94,10 @@ export function range(from, to) {
return new RangeIterable(from, to);
}

export function repeat(value, times) {
return new RepeatIterable(value, times);
}

export function from(source) {
const iterator = source[Symbol.iterator];
if (typeof iterator === 'function') {
Expand Down
28 changes: 28 additions & 0 deletions src/generators/repeat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { BaseLinqIterable } from "../base-linq-iterable";
export class RepeatIterable extends BaseLinqIterable {
constructor(value, times) {
super([]);
this.value = value;
this.times = times;
}

get() {
return this;
}

[Symbol.iterator]() {
let indx = 0;
const max = this.times;
const item = this.value;
return {
next() {
if (indx < max) {
indx++;
return { done: false, value: item };
} else {
return { done: true }
}
}
};
}
}
4 changes: 3 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { ConcatIterable } from "./iterables/concat";
import { UnionIterable } from "./iterables/union";
import { GroupJoinIterable } from "./iterables/group-join";
import { JoinIterable } from "./iterables/join";
import { RepeatIterable } from "./generators/repeat";

// note: if using class as output we can just apply the mixin to BaseLinqIterable.
applyMixin(linqMixin, [
Expand All @@ -26,6 +27,7 @@ applyMixin(linqMixin, [
TakeIterable,
SkipIterable,
RangeIterable,
RepeatIterable,
DistinctIterable,
Grouping,
GroupIterable,
Expand All @@ -37,4 +39,4 @@ applyMixin(linqMixin, [
JoinIterable,
]);

export { fromIterable, fromObject, fromArrayLike, range, from } from './creation';
export { fromIterable, fromObject, fromArrayLike, range, from, repeat } from './creation';
82 changes: 82 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,88 @@ export function quickSort(items, left, right, comparer) {
return copy;
}

export function __search(items, value, start, end, comparer) {
if (start > end) {
return -1;
}
const middle = Math.floor((start + end) / 2);
const result = comparer(items[middle], value);
if (result === 0) {
return middle;
}
if (result < 0) {
return __search(items, value, middle + 1, end, comparer);
} else {
return __search(items, value, start, middle - 1, comparer);
}
}

/**
* Do a binary search in ordered list
* @param items list
* @param value searched value
* @param comparer comparer function
* @return {number} index of the value we search.
*/
export function search(items, value, comparer) {
const start = 0;
const end = items.length - 1;
return __search(items, value, start, end, comparer);
}

export function __findInsertIndex(items, value, start, end, comparer) {
const middle = Math.floor((start + end) / 2);
const result = comparer(items[middle], value);
if (result === 0) {
// found same value, insert after it.
return middle + 1;
}
if (result < 0) {
// middle is smaller than value, check next if is bigger then return index if not continue searching.
if (middle + 1 === items.length) {
// middle is last item.
return items.length;
}
const nextResult = comparer(items[middle + 1], value);
if (nextResult === 0) {
// equal with next; // insert after that.
return middle + 2;
}
if (nextResult < 0) {
// next is still smaller
return __findInsertIndex(items, value, middle + 2, end, comparer);
} else {
// next is bigger.
return middle + 1;
}
} else {
// middle is bigger than value;
if (middle === 0) {
// middle is first
return 0;
}
const prevResult = comparer(items[middle - 1], value);
if (prevResult === 0 || prevResult < 0) {
// previous result is equal insert after that (before middle)
return middle;
} else {
return __findInsertIndex(items, value, 0, middle - 2, comparer);
}
}
}

export function insertOrdered(items, value, comparer) {
const start = 0;
const end = items.length;
if (start === end) {
items.splice(0, 0, value);
return items;
}
const insertIndex = __findInsertIndex(items, value, start, end, comparer);
items.splice(insertIndex, 0, value);
return items;
}

export class SetCheck {
constructor() {
this.set = new Set();
Expand Down
22 changes: 22 additions & 0 deletions test/benchmark/distinct.perf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Benchmark from "benchmark";
import { Person } from "../unit/models";
import { from, range, repeat } from "../../index.esm";

const iterable = new Set(repeat(0, 10000).concat(repeat(1, 100000)));
const array = Array.from(iterable);

export const distinctCompareIterableBenchmark = new Benchmark('[distinct] Distinct iterable with compare', () => {
from(iterable).distinct((a, b) => a === b).toArray();
});

export const distinctIterableBenchmark = new Benchmark('[distinct] Distinct iterable', () => {
from(iterable).distinct().toArray();
});

export const distinctCompareArrayBenchmark = new Benchmark('[distinct] Distinct array with compare', () => {
from(array).distinct((a, b) => a === b).toArray();
});

export const distinctArrayBenchmark = new Benchmark('[distinct] Distinct array', () => {
from(array).distinct().toArray();
});
5 changes: 5 additions & 0 deletions test/benchmark/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as whereBenches from './where.perf'
import * as takeBenches from './take.perf'
import * as skipBenches from './skip.perf'
import * as sortBenches from './sort.perf'
import * as distinctBenches from './distinct.perf';

import Benchmark from 'benchmark';

Expand All @@ -16,6 +17,7 @@ if (requested.length === 0) {
from(takeBenches).forEach(e => suit.add(e.value.name, e.value.fn));
from(skipBenches).forEach(e => suit.add(e.value.name, e.value.fn));
from(sortBenches).forEach(e => suit.add(e.value.name, e.value.fn));
from(distinctBenches).forEach(e => suit.add(e.value.name, e.value.fn));
} else {
if (requested.indexOf('--select') > -1) {
from(selectBenches).forEach(e => suit.add(e.value.name, e.value.fn));
Expand All @@ -32,6 +34,9 @@ if (requested.length === 0) {
if (requested.indexOf('--sort') > -1) {
from(sortBenches).forEach(e => suit.add(e.value.name, e.value.fn));
}
if (requested.indexOf('--distinct') > -1) {
from(distinctBenches).forEach(e => suit.add(e.value.name, e.value.fn));
}
}

suit.on('complete', function () {
Expand Down
12 changes: 3 additions & 9 deletions test/benchmark/select.perf.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
import { range, from } from '../../index.esm.js';
import chai from 'chai';
import Benchmark from 'benchmark';

const assert = chai.assert;

const arrayLength = 100000;
const iterable = new Set(range(0, arrayLength));
const array = Array.from(iterable);

export const arrayMapBenchmark = new Benchmark('[select] Array map', () => {
const res = array.map(_ => _ * 3);
assert.equal(res.length, arrayLength);
array.map(_ => _ * 3);
});

export const selectArrayInput = new Benchmark('[select] array input', () => {
const res = from(array).select(_ => _ * 3).toArray();
assert.equal(res.length, arrayLength);
from(array).select(_ => _ * 3).toArray();
});

export const selectIterableInput = new Benchmark('[select] iterable input', () => {
const res = from(iterable).select(_ => _ * 3).toArray();
assert.equal(res.length, arrayLength);
from(iterable).select(_ => _ * 3).toArray();
});
12 changes: 3 additions & 9 deletions test/benchmark/skip.perf.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
import { range, from } from '../../index.esm.js';
import chai from 'chai';
import Benchmark from 'benchmark';

const assert = chai.assert;
const arrayLength = 100000;
const lengthToSkip = 1000;
const expected = arrayLength - lengthToSkip;

const iterable = new Set(range(0, arrayLength));
const array = Array.from(iterable);

export const arraySliceBenchmark = new Benchmark('[skip] Array slice', () => {
const res = array.slice(lengthToSkip, arrayLength);
assert.equal(res.length, expected);
array.slice(lengthToSkip, arrayLength);
});

export const skipArrayInput = new Benchmark('[skip] array input', () => {
const res = from(array).skip(lengthToSkip).toArray();
assert.equal(res.length, expected);
from(array).skip(lengthToSkip).toArray();
});

export const skipIterableInput = new Benchmark('[skip] iterable input', () => {
const res = from(iterable).skip(lengthToSkip).toArray();
assert.equal(res.length, expected);
from(iterable).skip(lengthToSkip).toArray();
});

23 changes: 7 additions & 16 deletions test/benchmark/sort.perf.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { range, from } from '../../index.esm.js';
import chai from 'chai';
import Benchmark from 'benchmark';

const assert = chai.assert;
const arrayLength = 100000;
const iterable = new Set(range(arrayLength, 0));
const array = Array.from(iterable);
Expand All @@ -11,36 +9,29 @@ const iterable1 = new Set(range(0, arrayLength));
const array1 = Array.from(iterable1);

export const arraySortBenchmarkCompare = new Benchmark('[orderBy] Array sort with compare', () => {
const res = [...array].sort((a, b) => a - b);
assert.equal(res.length, arrayLength);
[...array].sort((a, b) => a - b);
});

export const arraySortBenchmark = new Benchmark('[orderBy] Array sort', () => {
const res = [...array].sort();
assert.equal(res.length, arrayLength);
[...array].sort();
});

export const orderByArrayInput = new Benchmark('[orderBy] array input', () => {
const res = from(array).orderBy(_ => _ ).toArray();
assert.equal(res.length, arrayLength);
from(array).orderBy(_ => _ ).toArray();
});

export const orderByArrayInputCompare = new Benchmark('[orderBy] array input with compare', () => {
const res = from(array).orderBy(_ => _, (a, b) => a - b).toArray();
assert.equal(res.length, arrayLength);
from(array).orderBy(_ => _, (a, b) => a - b).toArray();
});

export const orderByIterableInput = new Benchmark('[orderBy] iterable input', () => {
const res = from(iterable).orderBy(_ => _).toArray();
assert.equal(res.length, arrayLength);
from(iterable).orderBy(_ => _).toArray();
});

export const orderByDecreaseArrayInputCompare = new Benchmark('[orderBy] array input with compare (Descending)', () => {
const res = from(array1).orderByDescending(_ => _, (a, b) => a - b).toArray();
assert.equal(res.length, arrayLength);
from(array1).orderByDescending(_ => _, (a, b) => a - b).toArray();
});

export const orderByDecreaseIterableInput = new Benchmark('[orderBy] iterable input (Descending)', () => {
const res = from(iterable1).orderByDescending(_ => _).toArray();
assert.equal(res.length, arrayLength);
from(iterable1).orderByDescending(_ => _).toArray();
});
12 changes: 3 additions & 9 deletions test/benchmark/take.perf.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
import { range, from } from '../../index.esm.js';
import chai from 'chai';
import Benchmark from 'benchmark';

const assert = chai.assert;

const arrayLength = 100000;
const iterable = new Set(range(0, arrayLength));
const array = Array.from(iterable);
const lengthToTake = 1000;

export const arraySliceBenchmark = new Benchmark('[take] Array slice', () => {
const res = array.slice(0, lengthToTake);
assert.equal(res.length, lengthToTake);
array.slice(0, lengthToTake);
});

export const takeArrayInput = new Benchmark('[take] array input', () => {
const res = from(array).take(lengthToTake).toArray();
assert.equal(res.length, lengthToTake);
from(array).take(lengthToTake).toArray();
});

export const takeIterableInput = new Benchmark('[take] iterable input', () => {
const res = from(iterable).take(lengthToTake).toArray();
assert.equal(res.length, lengthToTake);
from(iterable).take(lengthToTake).toArray();
});


12 changes: 3 additions & 9 deletions test/benchmark/where.perf.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
import { range, from } from '../../index.esm.js';
import chai from 'chai';
import Benchmark from 'benchmark';

const assert = chai.assert;

const arrayLength = 100000;
const iterable = new Set(range(0, arrayLength));
const array = Array.from(iterable);

export const arrayFilterBenchmark = new Benchmark('[where] Array filter', () => {
const res = array.filter(_ => _ % 2 === 1);
assert.equal(res.length, arrayLength / 2);
array.filter(_ => _ % 2 === 1);
});

export const whereArrayInput = new Benchmark('[where] array input', () => {
const res = from(array).where(_ => _ % 2 === 1).toArray();
assert.equal(res.length, arrayLength / 2);
from(array).where(_ => _ % 2 === 1).toArray();
});

export const whereIterableInput = new Benchmark('[where] iterable input', () => {
const res = from(iterable).where(_ => _ % 2 === 1).toArray();
assert.equal(res.length, arrayLength / 2);
from(iterable).where(_ => _ % 2 === 1).toArray();
});
Loading

0 comments on commit 21f414a

Please sign in to comment.