Skip to content

Commit e7a81a7

Browse files
committed
Add defaultIfEmpty
1 parent 645112b commit e7a81a7

11 files changed

Lines changed: 169 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ To use library with ES6 modules,
120120
| concatenate | No | [Sync](tests/unittests/tests/Concatenate.ts) | Equivalent to `.Concat` but renamed to avoid conflict with JS
121121
| contains | Yes | [Sync](tests/unittests/tests/Contains.ts), [Async](tests/unittests/tests/ContainsAsync.ts)
122122
| count | Yes | [Sync](tests/unittests/tests/Count.ts), [Async](tests/unittests/tests/CountAsync.ts)
123+
| defaultIfEmpty | No | [Sync](tests/unittests/tests/DefaultIfEmpty.ts)
123124
| distinct | Yes | [Sync](tests/unittests/tests/Distinct.ts), [Async](tests/unittests/tests/DistinctAsync.ts)
124125
| elementAt | No | [Sync](tests/unittests/tests/ElementAt.ts)
125126
| elementAtOrDefault | No | [Sync](tests/unittests/tests/ElementAtOrDefault.ts)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { IAsyncEnumerable } from "../../types"
2+
import { BasicAsyncEnumerable } from "../BasicAsyncEnumerable"
3+
4+
export const defaultIfEmpty = <TSource>(source: AsyncIterable<TSource>, defaultValue: TSource | Promise<TSource>): IAsyncEnumerable<TSource> => {
5+
async function* generator() {
6+
let found = false
7+
for await (const value of source) {
8+
found = true
9+
yield value
10+
}
11+
12+
if (!found) {
13+
yield defaultValue
14+
}
15+
}
16+
17+
return new BasicAsyncEnumerable(generator)
18+
}

src/initializer/bindLinq.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { contains } from "./../sync/_private/contains"
1414
import { containsAsync } from "./../sync/_private/containsAsync"
1515
import { count } from "./../sync/_private/count"
1616
import { countAsync } from "./../sync/_private/countAsync"
17+
import { defaultIfEmpty } from "../sync/_private/defaultIfEmpty"
1718
import { distinct } from "./../sync/_private/distinct"
1819
import { distinctAsync } from "./../sync/_private/distinctAsync"
1920
import { each } from "./../sync/_private/each"
@@ -115,6 +116,7 @@ export const bindLinq = <T, Y extends Iterable<T>>(object: IPrototype<Y>) => {
115116
bind(containsAsync, "containsAsync")
116117
bind(count, "count")
117118
bind(countAsync, "countAsync")
119+
bind(defaultIfEmpty, "defaultIfEmpty")
118120
bind(distinct, "distinct")
119121
bind(distinctAsync, "distinctAsync")
120122
bind(each, "each")

src/initializer/bindLinqAsync.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { contains } from "./../async/_private/contains"
1313
import { containsAsync } from "./../async/_private/containsAsync"
1414
import { count } from "./../async/_private/count"
1515
import { countAsync } from "./../async/_private/countAsync"
16+
import { defaultIfEmpty } from "../async/_private/defaultIfEmpty"
1617
import { distinct } from "./../async/_private/distinct"
1718
import { distinctAsync } from "./../async/_private/distinctAsync"
1819
import { each } from "./../async/_private/each"
@@ -110,6 +111,7 @@ export const bindLinqAsync = <T, Y extends AsyncIterable<T>>(object: IPrototype<
110111
bind(containsAsync, "containsAsync")
111112
bind(count, "count")
112113
bind(countAsync, "countAsync")
114+
bind(defaultIfEmpty, "defaultIfEmpty")
113115
bind(distinct, "distinct")
114116
bind(distinctAsync, "distinctAsync")
115117
bind(each, "each")

src/initializer/bindLinqParallel.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { contains } from "./../parallel/_private/contains"
1313
import { containsAsync } from "./../parallel/_private/containsAsync"
1414
import { count } from "./../parallel/_private/count"
1515
import { countAsync } from "./../parallel/_private/countAsync"
16+
import { defaultIfEmpty } from "./../parallel/_private/defaultIfEmpty"
1617
import { distinct } from "./../parallel/_private/distinct"
1718
import { distinctAsync } from "./../parallel/_private/distinctAsync"
1819
import { each } from "./../parallel/_private/each"
@@ -109,6 +110,7 @@ export const bindLinqParallel = <T, Y extends AsyncIterable<T>>(object: IPrototy
109110
bind(containsAsync, "containsAsync")
110111
bind(count, "count")
111112
bind(countAsync, "countAsync")
113+
bind(defaultIfEmpty, "defaultIfEmpty")
112114
bind(distinct, "distinct")
113115
bind(distinctAsync, "distinctAsync")
114116
bind(each, "each")
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { IParallelEnumerable, ParallelGeneratorType, TypedData } from "../../types"
2+
import { BasicParallelEnumerable } from "../BasicParallelEnumerable"
3+
4+
export const defaultIfEmpty = <TSource>(source: IParallelEnumerable<TSource>, defaultValue: TSource | Promise<TSource>): IParallelEnumerable<TSource> => {
5+
const dataFunc = source.dataFunc
6+
const isPromise = defaultValue instanceof Promise
7+
let typeData: TypedData<TSource>
8+
switch (dataFunc.type) {
9+
case ParallelGeneratorType.PromiseToArray: {
10+
const generator = () => dataFunc
11+
.generator()
12+
.then((values) => {
13+
if (values.length) {
14+
return values
15+
}
16+
17+
if (isPromise) {
18+
return defaultValue.then(value => [value])
19+
}
20+
else {
21+
return [defaultValue]
22+
}
23+
})
24+
25+
typeData = {
26+
generator,
27+
type: dataFunc.type
28+
}
29+
}
30+
break
31+
case ParallelGeneratorType.ArrayOfPromises: {
32+
const generator = () => {
33+
const promises = dataFunc.generator()
34+
if (promises.length) {
35+
return promises
36+
}
37+
38+
if (isPromise) {
39+
return [defaultValue]
40+
}
41+
else {
42+
return [Promise.resolve(defaultValue)]
43+
}
44+
}
45+
46+
typeData = {
47+
generator,
48+
type: dataFunc.type
49+
}
50+
}
51+
break
52+
case ParallelGeneratorType.PromiseOfPromises: {
53+
const generator = async () => {
54+
const promises = await dataFunc.generator()
55+
if (promises.length) {
56+
return promises
57+
}
58+
59+
if (isPromise) {
60+
return [defaultValue]
61+
}
62+
else {
63+
return [Promise.resolve(defaultValue)]
64+
}
65+
}
66+
67+
typeData = {
68+
generator,
69+
type: dataFunc.type
70+
}
71+
}
72+
break
73+
}
74+
75+
return new BasicParallelEnumerable(typeData)
76+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { BasicEnumerable } from "../BasicEnumerable"
2+
3+
export const defaultIfEmpty = <TSource>(source: Iterable<TSource>, defaultValue: TSource) => {
4+
function* generator() {
5+
let found = false
6+
for (const value of source) {
7+
found = true
8+
yield value
9+
}
10+
11+
if (!found) {
12+
yield defaultValue
13+
}
14+
}
15+
16+
return new BasicEnumerable(generator)
17+
}

src/types/IAsyncEnumerable.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ export interface IAsyncEnumerable<TSource> extends IAsyncParallel<TSource> {
2424
* @returns An IAsyncEnumerable<T> that contains the concatenated elements of the two sequences.
2525
*/
2626
concatenate(second: IAsyncEnumerable<TSource>): IAsyncEnumerable<TSource>
27+
/**
28+
* Returns the elements of an IAsyncEnumerable<T>, or a default valued singleton collection if the sequence is empty.
29+
* @param defaultValue The value, or Promise that gives back that value, to return if the sequence is empty.
30+
* @returns An IAsyncEnumerable<T> that contains defaultValue if source is empty; otherwise, source.
31+
*/
32+
defaultIfEmpty(defaultValue: TSource | Promise<TSource>): IAsyncEnumerable<TSource>
2733
/**
2834
* Returns distinct elements from a sequence by using the default
2935
* or specified equality comparer to compare values.

src/types/IEnumerable.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,12 @@ export interface IEnumerable<TSource> extends Iterable<TSource> {
141141
* @returns The number of elements in the input sequence.
142142
*/
143143
countAsync(predicate: (x: TSource) => Promise<boolean>): Promise<number>
144+
/**
145+
* Returns the elements of an IEnumerable<T>, or a default valued singleton collection if the sequence is empty.
146+
* @param defaultValue The value to return if the sequence is empty.
147+
* @returns An IEnumerable<T> that contains defaultValue if source is empty; otherwise, source.
148+
*/
149+
defaultIfEmpty(defaultValue: TSource): IEnumerable<TSource>
144150
/**
145151
* Returns distinct elements from a sequence by using the default or specified equality comparer to compare values.
146152
* @param comparer An IEqualityComparer<T> to compare values. Optional. Defaults to Strict Equality Comparison.

src/types/IParallelEnumerable.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ export interface IParallelEnumerable<TSource> extends IAsyncParallel<TSource> {
3131
* @returns An IParallelEnumerable<T> that contains the concatenated elements of the two sequences.
3232
*/
3333
concatenate(second: IAsyncParallel<TSource>): IParallelEnumerable<TSource>
34+
/**
35+
* Returns the elements of an IParallelEnumerable<T>, or a default valued singleton collection if the sequence is empty.
36+
* @param defaultValue The value, or Promise that gives back that value, to return if the sequence is empty.
37+
* @returns An IParallelEnumerable<T> that contains defaultValue if source is empty; otherwise, source.
38+
*/
39+
defaultIfEmpty(defaultValue: TSource | Promise<TSource>): IParallelEnumerable<TSource>
3440
/**
3541
* Returns distinct elements from a sequence by using the default or specified equality comparer to compare values.
3642
* @param comparer An IEqualityComparer<T> to compare values. Optional. Defaults to Strict Equality Comparison.

0 commit comments

Comments
 (0)