Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestions to make it easier to exclude null and undefined #2

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 24 additions & 0 deletions packages/leseq/src/operators/async/filterNotNullAsync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {AsyncGen, AsyncOperator, AsyncSeq} from "../../asyncSeq";

/**
* Returns a sequence that excludes null and undefined from the current sequence.
*
* ```typescript
* const result = await fromAsAsync([1, 2, null, undefined, 5]).pipe(
* filterNotNull()
* ).toArrayAsync();
* //result: [1,2,5]
* ```
*
* @returns Operator function.
* @typeParam T Source element type.
* @category Operators
*/
export const filterNotNullAsync = <T>(): AsyncOperator<T, NonNullable<T>> =>
async function* filterNotNullAsync(source: AsyncSeq<T>): AsyncGen<NonNullable<T>> {
for await (const i of source) {
if (i !== null && i !== undefined) {
yield i!!;
}
}
};
2 changes: 2 additions & 0 deletions packages/leseq/src/operators/async/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { mapAsync } from './mapAsync';
export { mapNotNullAsync } from './mapNotNullAsync';
export { chunkAsync } from './chunkAsync';
export { scanAsync } from './scanAsync';
export { orderByAsync } from './orderByAsync';
Expand All @@ -8,6 +9,7 @@ export { uniqAsync } from './uniqAsync';
export { concatAsync } from './concatAsync';
export { concatValueAsync } from './concatValueAsync';
export { filterAsync } from './filterAsync';
export { filterNotNullAsync } from './filterNotNullAsync';
export { flattenAsync } from './flattenAsync';
export { takeAsync } from './takeAsync';
export { takeWhileAsync } from './takeWhileAsync';
Expand Down
31 changes: 31 additions & 0 deletions packages/leseq/src/operators/async/mapNotNullAsync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {AsyncGen, AsyncOperator, AsyncSeq} from "../../asyncSeq";

/**
* Returns the sequence in which each element has been transformed by the specified transformation function.
* However, null or undefined elements are excluded from the sequence.
*
* ```typescript
* const result = await fromAsAsync([1, 2, null, 4, undefined]).pipe(
* mapNotNull(i => i?.toString())
* ).toArrayAsync();
* //result: ["1", "2", "4"]
* ```
*
* @param func Transform function.
* @returns Operator function.
* @typeParam T Source element type.
* @typeParam TResult Transformed element type.
* @category Operators
*/
export const mapNotNullAsync = <T, TResult>(func: (arg: T, index: number) => Promise<TResult>): AsyncOperator<T, NonNullable<TResult>> =>
async function* mapNotNullAsync(source: AsyncSeq<T>): AsyncGen<NonNullable<TResult>> {
let count = 0;
for await (const i of source) {
const result = await func(i, count);
if (result !== null && result !== undefined) {
yield result!!;
}

count++;
}
};
22 changes: 22 additions & 0 deletions packages/leseq/src/operators/filterNotNull.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Gen, Operator, Seq } from '../seq';

/**
* Returns a sequence that excludes null and undefined from the current sequence.
*
* ```typescript
* const result = from([1, 2, null, undefined, 5]).pipe(filterNotNull()).toArray();
* //result: [1,2,5]
* ```
*
* @returns Operator function.
* @typeParam T Source element type.
* @category Operators
*/
export const filterNotNull = <T>(): Operator<T, NonNullable<T>> =>
function* filterNotNull(source: Seq<T>): Gen<NonNullable<T>> {
for (const i of source) {
if (i !== null && i !== undefined) {
yield i!!;
}
}
};
2 changes: 2 additions & 0 deletions packages/leseq/src/operators/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { map } from './map';
export { mapNotNull } from './mapNotNull';
export { chunk } from './chunk';
export { scan } from './scan';
export { orderBy } from './orderBy';
Expand All @@ -8,6 +9,7 @@ export { uniq } from './uniq';
export { concat } from './concat';
export { concatValue } from './concatValue';
export { filter } from './filter';
export { filterNotNull } from './filterNotNull';
export { flatten } from './flatten';
export { take } from './take';
export { takeWhile } from './takeWhile';
Expand Down
29 changes: 29 additions & 0 deletions packages/leseq/src/operators/mapNotNull.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Gen, Operator, Seq } from '../seq';

/**
* Returns the sequence in which each element has been transformed by the specified transformation function.
* However, null or undefined elements are excluded from the sequence.
*
* ```typescript
* const result = from([1, 2, null, 4, undefined]).pipe(mapNotNull(i => i?.toString())).toArray();
* //result: ["1", "2", "4"]
* ```
*
* @param func Transform function.
* @returns Operator function.
* @typeParam T Source element type.
* @typeParam TResult Transformed element type.
* @category Operators
*/
export const mapNotNull = <T, TResult>(func: (arg: T, index: number) => TResult): Operator<T, NonNullable<TResult>> =>
function* mapNotNull(source: Seq<T>): Gen<NonNullable<TResult>> {
let count = 0;
for (const i of source) {
const result = func(i, count);
if (result !== null && result !== undefined) {
yield result!!;
}

count++;
}
};
54 changes: 53 additions & 1 deletion packages/leseq/tests/async-operators.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
import { concat, concatValue, skip, skipWhile, filter, flatten, from, map, orderBy, take, takeWhile, tap, uniq, groupBy, chunk, scan, union, difference, intersect, reverse, concatAsync, fromAsAsync, tapAsync, concatValueAsync, skipAsync, skipWhileAsync, filterAsync, flattenAsync, mapAsync, orderByAsync, takeAsync, takeWhileAsync, uniqAsync, chunkAsync, scanAsync, groupByAsync, unionAsync, differenceAsync, intersectAsync, reverseAsync } from '../src';
import {
concatAsync,
fromAsAsync,
tapAsync,
concatValueAsync,
skipAsync,
skipWhileAsync,
filterAsync,
filterNotNullAsync,
flattenAsync,
mapAsync,
mapNotNullAsync,
orderByAsync,
takeAsync,
takeWhileAsync,
uniqAsync,
chunkAsync,
scanAsync,
groupByAsync,
unionAsync,
differenceAsync,
intersectAsync,
reverseAsync
} from '../src';
import { abortableSleep } from './testUtil';

test('operator: simple concatAsync', async () => {
Expand Down Expand Up @@ -58,6 +81,13 @@ test('operator: filterAsync index', async () => {
expect(indexes).toEqual([0, 1, 2, 3, 4, 5]);
});

test('operator: simple filterNotNullAsync', async () => {
const output = await fromAsAsync([2, 4, null, 5, null, undefined, 6, 7, 8])
.pipe(filterNotNullAsync())
.toArrayAsync();
expect(output).toEqual([2, 4, 5, 6, 7, 8]);
});

test('operator: simple flattenAsync', async () => {
const output = await fromAsAsync([
[1, 2],
Expand Down Expand Up @@ -110,6 +140,28 @@ test('operator: mapAsync index', async () => {
expect(indexes).toEqual([0, 1, 2]);
});

test('operator: simple mapNotNullAsync', async () => {
const output = await fromAsAsync([1, 2, null, 3, null, null, 4, undefined, 5])
.pipe(mapNotNullAsync(async i => i?.toString()))
.toArrayAsync();
expect(output).toEqual(["1", "2", "3", "4", "5"]);
});

test('operator: mapNotNullAsync index', async () => {
const indexes: number[] = [];
const output = await fromAsAsync([1, 2, null, 3, null, null, 4, undefined, 5])
.pipe(
mapNotNullAsync(async (i, index) => {
await abortableSleep(20);
indexes.push(index);
return i?.toString();
})
)
.toArrayAsync();
expect(output).toEqual(["1", "2", "3", "4", "5"]);
expect(indexes).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8]);
});

test('operator: orderByAsync asc', async () => {
const output = await fromAsAsync([6, 3, 2, 1, 4, 5])
.pipe(orderByAsync(i => i))
Expand Down
53 changes: 52 additions & 1 deletion packages/leseq/tests/operators.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
import { concat, concatValue, skip, skipWhile, filter, flatten, from, map, orderBy, take, takeWhile, tap, uniq, groupBy, chunk, scan, union, difference, intersect, reverse } from '../src';
import {
concat,
concatValue,
skip,
skipWhile,
filter,
filterNotNull,
flatten,
from,
map,
mapNotNull,
orderBy,
take,
takeWhile,
tap,
uniq,
groupBy,
chunk,
scan,
union,
difference,
intersect,
reverse
} from '../src';

test('operator: simple concat', () => {
const output = from([1, 2, 3, 4, 5])
Expand Down Expand Up @@ -50,6 +73,13 @@ test('operator: filter index', () => {
expect(indexes).toEqual([0, 1, 2, 3, 4, 5]);
});

test('operator: simple filterNotNull', () => {
const output = from([2, 4, null, 5, null, undefined, 6, 7, 8])
.pipe(filterNotNull())
.toArray();
expect(output).toEqual([2, 4, 5, 6, 7, 8]);
});

test('operator: simple flatten', () => {
const output = from([
[1, 2],
Expand Down Expand Up @@ -100,6 +130,27 @@ test('operator: map index', () => {
expect(indexes).toEqual([0, 1, 2]);
});

test('operator: simple mapNotNull', () => {
const output = from([1, 2, null, 3, null, null, 4, undefined, 5])
.pipe(mapNotNull(i => i?.toString()))
.toArray();
expect(output).toEqual(["1", "2", "3", "4", "5"]);
});

test('operator: mapNotNull index', () => {
const indexes: number[] = [];
const output = from([1, 2, null, 3, null, null, 4, undefined, 5])
.pipe(
mapNotNull((i, index) => {
indexes.push(index);
return i?.toString();
})
)
.toArray();
expect(output).toEqual(["1", "2", "3", "4", "5"]);
expect(indexes).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8]);
});

test('operator: orderBy asc', () => {
const output = from([6, 3, 2, 1, 4, 5])
.pipe(orderBy(i => i))
Expand Down