Add concat
iterable utility
This commit is contained in:
parent
028b4f162b
commit
7c7f7ee5a0
3 changed files with 98 additions and 5 deletions
|
@ -4,7 +4,14 @@
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import * as sinon from 'sinon';
|
import * as sinon from 'sinon';
|
||||||
|
|
||||||
import { isIterable, size, filter, map, take } from '../../util/iterables';
|
import {
|
||||||
|
concat,
|
||||||
|
filter,
|
||||||
|
isIterable,
|
||||||
|
map,
|
||||||
|
size,
|
||||||
|
take,
|
||||||
|
} from '../../util/iterables';
|
||||||
|
|
||||||
describe('iterable utilities', () => {
|
describe('iterable utilities', () => {
|
||||||
describe('isIterable', () => {
|
describe('isIterable', () => {
|
||||||
|
@ -82,6 +89,72 @@ describe('iterable utilities', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('concat', () => {
|
||||||
|
it('returns an empty iterable when passed nothing', () => {
|
||||||
|
assert.deepEqual([...concat()], []);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an empty iterable when passed empty iterables', () => {
|
||||||
|
assert.deepEqual([...concat([])], []);
|
||||||
|
assert.deepEqual([...concat(new Set())], []);
|
||||||
|
assert.deepEqual([...concat(new Set(), [], new Map())], []);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('concatenates multiple iterables', () => {
|
||||||
|
const everyNumber = {
|
||||||
|
*[Symbol.iterator]() {
|
||||||
|
for (let i = 4; true; i += 1) {
|
||||||
|
yield i;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = concat([1, 2], new Set([3]), [], everyNumber);
|
||||||
|
const iterator = result[Symbol.iterator]();
|
||||||
|
|
||||||
|
assert.deepEqual(iterator.next(), { value: 1, done: false });
|
||||||
|
assert.deepEqual(iterator.next(), { value: 2, done: false });
|
||||||
|
assert.deepEqual(iterator.next(), { value: 3, done: false });
|
||||||
|
assert.deepEqual(iterator.next(), { value: 4, done: false });
|
||||||
|
assert.deepEqual(iterator.next(), { value: 5, done: false });
|
||||||
|
assert.deepEqual(iterator.next(), { value: 6, done: false });
|
||||||
|
assert.deepEqual(iterator.next(), { value: 7, done: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("doesn't start the iterable until the last minute", () => {
|
||||||
|
const oneTwoThree = {
|
||||||
|
[Symbol.iterator]: sinon.fake(() => {
|
||||||
|
let n = 0;
|
||||||
|
return {
|
||||||
|
next() {
|
||||||
|
if (n > 3) {
|
||||||
|
return { done: true };
|
||||||
|
}
|
||||||
|
n += 1;
|
||||||
|
return { value: n, done: false };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = concat([1, 2], oneTwoThree);
|
||||||
|
const iterator = result[Symbol.iterator]();
|
||||||
|
|
||||||
|
sinon.assert.notCalled(oneTwoThree[Symbol.iterator]);
|
||||||
|
|
||||||
|
iterator.next();
|
||||||
|
sinon.assert.notCalled(oneTwoThree[Symbol.iterator]);
|
||||||
|
iterator.next();
|
||||||
|
sinon.assert.notCalled(oneTwoThree[Symbol.iterator]);
|
||||||
|
|
||||||
|
iterator.next();
|
||||||
|
sinon.assert.calledOnce(oneTwoThree[Symbol.iterator]);
|
||||||
|
|
||||||
|
iterator.next();
|
||||||
|
sinon.assert.calledOnce(oneTwoThree[Symbol.iterator]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('filter', () => {
|
describe('filter', () => {
|
||||||
it('returns an empty iterable when passed an empty iterable', () => {
|
it('returns an empty iterable when passed an empty iterable', () => {
|
||||||
const fn = sinon.fake();
|
const fn = sinon.fake();
|
||||||
|
|
|
@ -48,6 +48,7 @@ import {
|
||||||
LinkPreviewImage,
|
LinkPreviewImage,
|
||||||
LinkPreviewMetadata,
|
LinkPreviewMetadata,
|
||||||
} from '../linkPreviews/linkPreviewFetch';
|
} from '../linkPreviews/linkPreviewFetch';
|
||||||
|
import { concat } from '../util/iterables';
|
||||||
|
|
||||||
function stringToArrayBuffer(str: string): ArrayBuffer {
|
function stringToArrayBuffer(str: string): ArrayBuffer {
|
||||||
if (typeof str !== 'string') {
|
if (typeof str !== 'string') {
|
||||||
|
@ -1706,10 +1707,12 @@ export default class MessageSender {
|
||||||
isNotMe = r => r !== myE164;
|
isNotMe = r => r !== myE164;
|
||||||
}
|
}
|
||||||
|
|
||||||
const blockedIdentifiers = new Set([
|
const blockedIdentifiers = new Set(
|
||||||
...window.storage.getBlockedUuids(),
|
concat(
|
||||||
...window.storage.getBlockedNumbers(),
|
window.storage.getBlockedUuids(),
|
||||||
]);
|
window.storage.getBlockedNumbers()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
const recipients = groupMembers.filter(
|
const recipients = groupMembers.filter(
|
||||||
recipient => isNotMe(recipient) && !blockedIdentifiers.has(recipient)
|
recipient => isNotMe(recipient) && !blockedIdentifiers.has(recipient)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
/* eslint-disable max-classes-per-file */
|
/* eslint-disable max-classes-per-file */
|
||||||
|
/* eslint-disable no-restricted-syntax */
|
||||||
|
|
||||||
export function isIterable(value: unknown): value is Iterable<unknown> {
|
export function isIterable(value: unknown): value is Iterable<unknown> {
|
||||||
return (
|
return (
|
||||||
|
@ -28,6 +29,22 @@ export function size(iterable: Iterable<unknown>): number {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function concat<T>(
|
||||||
|
...iterables: ReadonlyArray<Iterable<T>>
|
||||||
|
): Iterable<T> {
|
||||||
|
return new ConcatIterable(iterables);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConcatIterable<T> implements Iterable<T> {
|
||||||
|
constructor(private readonly iterables: ReadonlyArray<Iterable<T>>) {}
|
||||||
|
|
||||||
|
*[Symbol.iterator](): Iterator<T> {
|
||||||
|
for (const iterable of this.iterables) {
|
||||||
|
yield* iterable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function filter<T, S extends T>(
|
export function filter<T, S extends T>(
|
||||||
iterable: Iterable<T>,
|
iterable: Iterable<T>,
|
||||||
predicate: (value: T) => value is S
|
predicate: (value: T) => value is S
|
||||||
|
|
Loading…
Reference in a new issue