Skip to content

Commit d3dcc9e

Browse files
committed
immutable [nfc]: Move some helpers to a general location
We'll reuse some of these in other models as we use Immutable more.
1 parent 37b0939 commit d3dcc9e

File tree

3 files changed

+105
-100
lines changed

3 files changed

+105
-100
lines changed

src/immutableUtils.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/** General helpers to augment Immutable.js. */
2+
// @flow strict-local
3+
4+
import Immutable from 'immutable';
5+
6+
// Like `Immutable.Map#update`, but prune returned values equal to `zero`.
7+
export function updateAndPrune<K, V>(
8+
map: Immutable.Map<K, V>,
9+
zero: V,
10+
key: K,
11+
updater: (V | void) => V,
12+
): Immutable.Map<K, V> {
13+
const value = map.get(key);
14+
const newValue = updater(value);
15+
if (newValue === zero) {
16+
return map.delete(key);
17+
}
18+
if (newValue === value) {
19+
return map;
20+
}
21+
return map.set(key, newValue);
22+
}
23+
24+
// Like `Immutable.Map#map`, but with the update-only-if-different semantics
25+
// of `Immutable.Map#update`. Kept for comparison to `updateAllAndPrune`.
26+
export function updateAll<K, V>(map: Immutable.Map<K, V>, updater: V => V): Immutable.Map<K, V> {
27+
return map.withMutations(mapMut => {
28+
map.forEach((value, key) => {
29+
const newValue = updater(value);
30+
if (newValue !== value) {
31+
mapMut.set(key, newValue);
32+
}
33+
});
34+
});
35+
}
36+
37+
// Like `updateAll`, but prune values equal to `zero` given by `updater`.
38+
export function updateAllAndPrune<K, V>(
39+
map: Immutable.Map<K, V>,
40+
zero: V,
41+
updater: V => V,
42+
): Immutable.Map<K, V> {
43+
return map.withMutations(mapMut => {
44+
map.forEach((value, key) => {
45+
const newValue = updater(value);
46+
if (newValue === zero) {
47+
mapMut.delete(key);
48+
return;
49+
}
50+
if (newValue === value) {
51+
return; // i.e., continue
52+
}
53+
mapMut.set(key, newValue);
54+
});
55+
});
56+
}
57+
58+
/**
59+
* The union of sets, represented as sorted lists.
60+
*
61+
* The inputs must be sorted (by `<`) and without duplicates (by `===`).
62+
*
63+
* The output will contain all the elements found in either input, again
64+
* sorted and without duplicates.
65+
*/
66+
// TODO: This implementation is Θ(n log n), because it repeatedly looks up
67+
// elements by numerical index. It would be nice to instead use cursors
68+
// within the tree to get an O(n) implementation.
69+
export function setUnion<T: number>(
70+
xs: Immutable.List<T>,
71+
ys: Immutable.List<T>,
72+
): Immutable.List<T> {
73+
// TODO: Perhaps build a List directly, with setSize up front.
74+
const result = [];
75+
let i = 0;
76+
let x = xs.get(i++);
77+
let j = 0;
78+
let y = ys.get(j++);
79+
while (x !== undefined && y !== undefined) {
80+
if (x < y) {
81+
result.push(x);
82+
x = xs.get(i++);
83+
} else if (x !== y) {
84+
result.push(y);
85+
y = ys.get(j++);
86+
} else {
87+
// x === y
88+
result.push(x);
89+
x = xs.get(i++);
90+
y = ys.get(j++);
91+
}
92+
}
93+
while (x !== undefined) {
94+
result.push(x);
95+
x = xs.get(i++);
96+
}
97+
while (y !== undefined) {
98+
result.push(y);
99+
y = ys.get(j++);
100+
}
101+
return Immutable.List(result);
102+
}

src/unread/__tests__/unreadModel-test.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
import Immutable from 'immutable';
33

44
import { EVENT_UPDATE_MESSAGE_FLAGS } from '../../actionConstants';
5-
import { reducer, setUnion } from '../unreadModel';
5+
import { reducer } from '../unreadModel';
6+
import { setUnion } from '../../immutableUtils';
67
import { type UnreadState } from '../unreadModelTypes';
78
import * as eg from '../../__tests__/lib/exampleData';
89
import { initialState, makeUnreadState } from './unread-testlib';

src/unread/unreadModel.js

Lines changed: 1 addition & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
} from '../actionConstants';
2929
import DefaultMap from '../utils/DefaultMap';
3030
import * as logging from '../utils/logging';
31+
import { updateAllAndPrune, updateAndPrune, setUnion } from '../immutableUtils';
3132

3233
//
3334
//
@@ -91,105 +92,6 @@ export const getUnreadIdsForPmNarrow = (
9192

9293
const initialStreamsState: UnreadStreamsState = Immutable.Map();
9394

94-
// Like `Immutable.Map#update`, but prune returned values equal to `zero`.
95-
function updateAndPrune<K, V>(
96-
map: Immutable.Map<K, V>,
97-
zero: V,
98-
key: K,
99-
updater: (V | void) => V,
100-
): Immutable.Map<K, V> {
101-
const value = map.get(key);
102-
const newValue = updater(value);
103-
if (newValue === zero) {
104-
return map.delete(key);
105-
}
106-
if (newValue === value) {
107-
return map;
108-
}
109-
return map.set(key, newValue);
110-
}
111-
112-
// Like `Immutable.Map#map`, but with the update-only-if-different semantics
113-
// of `Immutable.Map#update`. Kept for comparison to `updateAllAndPrune`.
114-
/* eslint-disable-next-line no-unused-vars */
115-
function updateAll<K, V>(map: Immutable.Map<K, V>, updater: V => V): Immutable.Map<K, V> {
116-
return map.withMutations(mapMut => {
117-
map.forEach((value, key) => {
118-
const newValue = updater(value);
119-
if (newValue !== value) {
120-
mapMut.set(key, newValue);
121-
}
122-
});
123-
});
124-
}
125-
126-
// Like `updateAll`, but prune values equal to `zero` given by `updater`.
127-
function updateAllAndPrune<K, V>(
128-
map: Immutable.Map<K, V>,
129-
zero: V,
130-
updater: V => V,
131-
): Immutable.Map<K, V> {
132-
return map.withMutations(mapMut => {
133-
map.forEach((value, key) => {
134-
const newValue = updater(value);
135-
if (newValue === zero) {
136-
mapMut.delete(key);
137-
return;
138-
}
139-
if (newValue === value) {
140-
return; // i.e., continue
141-
}
142-
mapMut.set(key, newValue);
143-
});
144-
});
145-
}
146-
147-
/**
148-
* The union of sets, represented as sorted lists.
149-
*
150-
* The inputs must be sorted (by `<`) and without duplicates (by `===`).
151-
*
152-
* The output will contain all the elements found in either input, again
153-
* sorted and without duplicates.
154-
*/
155-
// TODO: This implementation is Θ(n log n), because it repeatedly looks up
156-
// elements by numerical index. It would be nice to instead use cursors
157-
// within the tree to get an O(n) implementation.
158-
export function setUnion<T: number>(
159-
xs: Immutable.List<T>,
160-
ys: Immutable.List<T>,
161-
): Immutable.List<T> {
162-
// TODO: Perhaps build a List directly, with setSize up front.
163-
const result = [];
164-
let i = 0;
165-
let x = xs.get(i++);
166-
let j = 0;
167-
let y = ys.get(j++);
168-
while (x !== undefined && y !== undefined) {
169-
if (x < y) {
170-
result.push(x);
171-
x = xs.get(i++);
172-
} else if (x !== y) {
173-
result.push(y);
174-
y = ys.get(j++);
175-
} else {
176-
// x === y
177-
result.push(x);
178-
x = xs.get(i++);
179-
y = ys.get(j++);
180-
}
181-
}
182-
while (x !== undefined) {
183-
result.push(x);
184-
x = xs.get(i++);
185-
}
186-
while (y !== undefined) {
187-
result.push(y);
188-
y = ys.get(j++);
189-
}
190-
return Immutable.List(result);
191-
}
192-
19395
function deleteMessagesIn(
19496
state: UnreadStreamsState,
19597
streamId: number,

0 commit comments

Comments
 (0)