From 4495a1ac67532a4703afd0a738ae4a75dd4411c3 Mon Sep 17 00:00:00 2001 From: Evan Hahn <69474926+EvanHahn-Signal@users.noreply.github.com> Date: Mon, 28 Jun 2021 16:46:33 -0500 Subject: [PATCH] Add `reduce` iterables utility --- ts/ConversationController.ts | 5 +++-- ts/test-both/util/iterables_test.ts | 18 ++++++++++++++++++ ts/util/iterables.ts | 12 ++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/ts/ConversationController.ts b/ts/ConversationController.ts index bb028b1997..9f3aeee133 100644 --- a/ts/ConversationController.ts +++ b/ts/ConversationController.ts @@ -1,7 +1,7 @@ // Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import { debounce, reduce, uniq, without } from 'lodash'; +import { debounce, uniq, without } from 'lodash'; import PQueue from 'p-queue'; import dataInterface from './sql/Client'; @@ -13,6 +13,7 @@ import { SendOptionsType, CallbackResultType } from './textsecure/SendMessage'; import { ConversationModel } from './models/conversations'; import { maybeDeriveGroupV2Id } from './groups'; import { assert } from './util/assert'; +import { map, reduce } from './util/iterables'; import { isGroupV1, isGroupV2 } from './util/whatTypeOfConversation'; import { deprecated } from './util/deprecated'; import { getSendOptions } from './util/getSendOptions'; @@ -103,7 +104,7 @@ export function start(): void { }; const newUnreadCount = reduce( - this.map((m: ConversationModel) => + map(this, (m: ConversationModel) => canCount(m) ? getUnreadCount(m) : 0 ), (item: number, memo: number) => (item || 0) + memo, diff --git a/ts/test-both/util/iterables_test.ts b/ts/test-both/util/iterables_test.ts index 31299f871b..02b02c7197 100644 --- a/ts/test-both/util/iterables_test.ts +++ b/ts/test-both/util/iterables_test.ts @@ -11,6 +11,7 @@ import { groupBy, isIterable, map, + reduce, size, take, } from '../../util/iterables'; @@ -311,6 +312,23 @@ describe('iterable utilities', () => { }); }); + describe('reduce', () => { + it('returns the accumulator when passed an empty iterable', () => { + const fn = sinon.fake(); + + assert.strictEqual(reduce([], fn, 123), 123); + + sinon.assert.notCalled(fn); + }); + + it('iterates over the iterable, ultimately returning a result', () => { + assert.strictEqual( + reduce(new Set([1, 2, 3, 4]), (result, n) => result + n, 89), + 99 + ); + }); + }); + describe('take', () => { it('returns the first n elements from an iterable', () => { const everyNumber = { diff --git a/ts/util/iterables.ts b/ts/util/iterables.ts index d390e53fc9..10897814c3 100644 --- a/ts/util/iterables.ts +++ b/ts/util/iterables.ts @@ -155,6 +155,18 @@ class MapIterator implements Iterator { } } +export function reduce( + iterable: Iterable, + fn: (result: TResult, value: T) => TResult, + accumulator: TResult +): TResult { + let result = accumulator; + for (const value of iterable) { + result = fn(result, value); + } + return result; +} + export function take(iterable: Iterable, amount: number): Iterable { return new TakeIterable(iterable, amount); }