Skip to content
This repository was archived by the owner on Jul 23, 2021. It is now read-only.

Commit ad0356b

Browse files
committed
Merge remote-tracking branch 'origin/main' into jd-feat-tsMapObject
2 parents 0e9da4d + f88e4f9 commit ad0356b

28 files changed

+1196
-183
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
name: Release
22

33
on:
4+
workflow_dispatch: ~
45
release:
56
types: [published]
67

CHANGELOG.md

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77
Dates are formatted as YYYY-MM-DD.
88

9-
## [Unreleased]
9+
## Unreleased
1010

1111
### Changed
1212

@@ -38,7 +38,6 @@ and the return type of `m.get('length')` is typed as `number`.
3838
The return of `m.get('inexistant')` throw the TypeScript error:
3939

4040
> Argument of type '"inexistant"' is not assignable to parameter of type '1 | "length"
41-
4241
#### If you want to keep the old definition
4342

4443
**This is a minor BC for TS users**, so if you want to keep the old definition, you can declare you Map like this:
@@ -57,7 +56,6 @@ type MyMapType = {
5756
1: string | null;
5857
optionalProperty?: string;
5958
};
60-
6159
const m = Map<MyMapType>({ length: 3, 1: 'one' });
6260
```
6361

@@ -79,6 +77,30 @@ Map<{ a?: string }>({ a: 'a' }).delete('a'); // you can only delete an optional
7977

8078
For now, only `get`, `getIn`, `set`, `update`, `delete` and `remove` methods are implemented. All other methods will fallback to the basic `Map` definition. Other method definition will be added later, but as some might be really complex, we prefer the progressive enhancement on the most used functions.
8179

80+
81+
82+
## [4.2.4] - 2023-02-06
83+
84+
- Improve type infererence for from JS by [KSXGitHub](https://github.com/KSXGitHub) [#1927](https://github.com/immutable-js/immutable-js/pull/1927)
85+
86+
## [4.2.3] - 2023-02-02
87+
88+
- TypeScript: `groupBy` return either a `Map` or an `OrderedMap`: make the type more precise than base `Collection` [#1924](https://github.com/immutable-js/immutable-js/pull/1924)
89+
90+
## [4.2.2] - 2023-01-02
91+
92+
- [Flow] Add type for `partition` method [#1920](https://github.com/immutable-js/immutable-js/pull/1920) by [Dagur](https://github.com/Dagur)
93+
94+
## [4.2.1] - 2022-12-23
95+
96+
- [Typescript] rollback some of the change on `toJS` to avoir circular reference
97+
98+
## [4.2.0] - 2022-12-22
99+
100+
- [TypeScript] Better type for toJS [#1917](https://github.com/immutable-js/immutable-js/pull/1917) by [jdeniau](https://github.com/jdeniau)
101+
- [TS Minor Break] tests are ran with TS > 4.5 only. It was tested with TS > 2.1 previously, but we want to level up TS types with recent features. TS 4.5 has been released more than one year before this release. If it does break your implementation (it might not), you should probably consider upgrading to the latest TS version.
102+
- Added a `partition` method to all containers [#1916](https://github.com/immutable-js/immutable-js/pull/1916) by [johnw42](https://github.com/johnw42)
103+
82104
## [4.1.0] - 2022-05-23
83105

84106
- Accept Symbol as Map key. [#1859](https://github.com/immutable-js/immutable-js/pull/1859) by [jdeniau](https://github.com/jdeniau)

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,46 @@ Range(1, Infinity)
637637
// 1006008
638638
```
639639

640+
## Comparison of filter(), groupBy(), and partition()
641+
642+
The `filter()`, `groupBy()`, and `partition()` methods are similar in that they
643+
all divide a collection into parts based on applying a function to each element.
644+
All three call the predicate or grouping function once for each item in the
645+
input collection. All three return zero or more collections of the same type as
646+
their input. The returned collections are always distinct from the input
647+
(according to `===`), even if the contents are identical.
648+
649+
Of these methods, `filter()` is the only one that is lazy and the only one which
650+
discards items from the input collection. It is the simplest to use, and the
651+
fact that it returns exactly one collection makes it easy to combine with other
652+
methods to form a pipeline of operations.
653+
654+
The `partition()` method is similar to an eager version of `filter()`, but it
655+
returns two collections; the first contains the items that would have been
656+
discarded by `filter()`, and the second contains the items that would have been
657+
kept. It always returns an array of exactly two collections, which can make it
658+
easier to use than `groupBy()`. Compared to making two separate calls to
659+
`filter()`, `partition()` makes half as many calls it the predicate passed to
660+
it.
661+
662+
The `groupBy()` method is a more generalized version of `partition()` that can
663+
group by an arbitrary function rather than just a predicate. It returns a map
664+
with zero or more entries, where the keys are the values returned by the
665+
grouping function, and the values are nonempty collections of the corresponding
666+
arguments. Although `groupBy()` is more powerful than `partition()`, it can be
667+
harder to use because it is not always possible predict in advance how many
668+
entries the returned map will have and what their keys will be.
669+
670+
| Summary | `filter` | `partition` | `groupBy` |
671+
|:------------------------------|:---------|:------------|:---------------|
672+
| ease of use | easiest | moderate | hardest |
673+
| generality | least | moderate | most |
674+
| laziness | lazy | eager | eager |
675+
| # of returned sub-collections | 1 | 2 | 0 or more |
676+
| sub-collections may be empty | yes | yes | no |
677+
| can discard items | yes | no | no |
678+
| wrapping container | none | array | Map/OrderedMap |
679+
640680
## Additional Tools and Resources
641681

642682
- [Atom-store](https://github.com/jameshopkins/atom-store/)

__tests__/KeyedSeq.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,23 @@ describe('KeyedSeq', () => {
3232
[3, 26],
3333
[4, 28],
3434
]);
35+
const [indexed0, indexed1] = seq
36+
.partition(isEven)
37+
.map(part => part.skip(10).take(5));
38+
expect(indexed0.entrySeq().toArray()).toEqual([
39+
[0, 21],
40+
[1, 23],
41+
[2, 25],
42+
[3, 27],
43+
[4, 29],
44+
]);
45+
expect(indexed1.entrySeq().toArray()).toEqual([
46+
[0, 20],
47+
[1, 22],
48+
[2, 24],
49+
[3, 26],
50+
[4, 28],
51+
]);
3552

3653
// Where Keyed Sequences maintain keys.
3754
const keyed = seq.toKeyedSeq();
@@ -43,6 +60,23 @@ describe('KeyedSeq', () => {
4360
[26, 26],
4461
[28, 28],
4562
]);
63+
const [keyed0, keyed1] = keyed
64+
.partition(isEven)
65+
.map(part => part.skip(10).take(5));
66+
expect(keyed0.entrySeq().toArray()).toEqual([
67+
[21, 21],
68+
[23, 23],
69+
[25, 25],
70+
[27, 27],
71+
[29, 29],
72+
]);
73+
expect(keyed1.entrySeq().toArray()).toEqual([
74+
[20, 20],
75+
[22, 22],
76+
[24, 24],
77+
[26, 26],
78+
[28, 28],
79+
]);
4680
});
4781

4882
it('works with reverse', () => {

__tests__/List.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,17 @@ describe('List', () => {
569569
expect(r.toArray()).toEqual(['b', 'd', 'f']);
570570
});
571571

572+
it('partitions values', () => {
573+
const v = List.of('a', 'b', 'c', 'd', 'e', 'f');
574+
const r = v
575+
.partition((value, index) => index % 2 === 1)
576+
.map(part => part.toArray());
577+
expect(r).toEqual([
578+
['a', 'c', 'e'],
579+
['b', 'd', 'f'],
580+
]);
581+
});
582+
572583
it('filters values based on type', () => {
573584
class A {}
574585
class B extends A {
@@ -588,6 +599,29 @@ describe('List', () => {
588599
expect(l2.every(v => v instanceof C)).toBe(true);
589600
});
590601

602+
it('partitions values based on type', () => {
603+
class A {}
604+
class B extends A {
605+
b(): void {
606+
return;
607+
}
608+
}
609+
class C extends A {
610+
c(): void {
611+
return;
612+
}
613+
}
614+
const l1 = List<A>([new B(), new C(), new B(), new C()]);
615+
// tslint:disable-next-line:arrow-parens
616+
const [la, lc]: [List<A>, List<C>] = l1.partition(
617+
(v): v is C => v instanceof C
618+
);
619+
expect(la.size).toEqual(2);
620+
expect(la.some(v => v instanceof C)).toBe(false);
621+
expect(lc.size).toEqual(2);
622+
expect(lc.every(v => v instanceof C)).toBe(true);
623+
});
624+
591625
it('reduces values', () => {
592626
const v = List.of(1, 10, 100);
593627
const r = v.reduce<number>((reduction, value) => reduction + value);

__tests__/Map.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,17 @@ describe('Map', () => {
304304
expect(r.toObject()).toEqual({ b: 2, d: 4, f: 6 });
305305
});
306306

307+
it('partitions values', () => {
308+
const m = Map({ a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 });
309+
const r = m
310+
.partition(value => value % 2 === 1)
311+
.map(part => part.toObject());
312+
expect(r).toEqual([
313+
{ b: 2, d: 4, f: 6 },
314+
{ a: 1, c: 3, e: 5 },
315+
]);
316+
});
317+
307318
it('derives keys', () => {
308319
const v = Map({ a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 });
309320
expect(v.keySeq().toArray()).toEqual(['a', 'b', 'c', 'd', 'e', 'f']);

__tests__/MultiRequire.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ jest.resetModules();
55
const Immutable2 = require('../src/Immutable');
66

77
describe('MultiRequire', () => {
8-
it('might require two different instances of Immutable', () => {
8+
it.skip('might require two different instances of Immutable', () => {
99
expect(Immutable1).not.toBe(Immutable2);
1010
expect(Immutable1.Map({ a: 1 }).toJS()).toEqual({ a: 1 });
1111
expect(Immutable2.Map({ a: 1 }).toJS()).toEqual({ a: 1 });

__tests__/Range.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,16 @@ describe('Range', () => {
148148
expect(r.toArray()).toEqual([0, 2, 4, 6, 8]);
149149
});
150150

151+
it('partitions values', () => {
152+
const r = Range(0, 10)
153+
.partition(v => v % 2 === 0)
154+
.map(part => part.toArray());
155+
expect(r).toEqual([
156+
[1, 3, 5, 7, 9],
157+
[0, 2, 4, 6, 8],
158+
]);
159+
});
160+
151161
it('reduces values', () => {
152162
const v = Range(0, 10, 2);
153163
const r = v.reduce<number>((a, b) => a + b, 0);

__tests__/Set.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ describe('Set', () => {
131131
expect(Set.intersect([])).toBe(Set());
132132
});
133133

134+
it('concatenates strings using union', () => {
135+
const s = Set(['one', 'two']);
136+
expect(s.union('three').toArray()).toEqual(['one', 'two', 'three']);
137+
});
138+
134139
it('iterates values', () => {
135140
const s = Set([1, 2, 3]);
136141
const iterator = jest.fn();

__tests__/groupBy.ts

Lines changed: 71 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,53 @@
1-
import { Collection, Map, Seq } from 'immutable';
1+
import {
2+
Collection,
3+
Map,
4+
Seq,
5+
isOrdered,
6+
OrderedMap,
7+
List,
8+
OrderedSet,
9+
Set,
10+
Stack,
11+
Record,
12+
} from 'immutable';
213

314
describe('groupBy', () => {
15+
it.each`
16+
constructor | constructorIsOrdered | isObject
17+
${Collection} | ${true} | ${false}
18+
${List} | ${true} | ${false}
19+
${Seq} | ${true} | ${false}
20+
${Set} | ${false} | ${false}
21+
${Stack} | ${true} | ${false}
22+
${OrderedSet} | ${true} | ${false}
23+
${Map} | ${false} | ${true}
24+
${OrderedMap} | ${true} | ${true}
25+
`(
26+
'groupBy returns ordered or unordered of the base type is ordered or not: $constructor.name',
27+
({ constructor, constructorIsOrdered, isObject }) => {
28+
const iterableConstructor = ['a', 'b', 'a', 'c'];
29+
const objectConstructor = { a: 1, b: 2, c: 3, d: 1 };
30+
31+
const col = constructor(
32+
isObject ? objectConstructor : iterableConstructor
33+
);
34+
35+
const grouped = col.groupBy(v => v);
36+
37+
// all groupBy should be instance of Map
38+
expect(grouped).toBeInstanceOf(Map);
39+
40+
// ordered objects should be instance of OrderedMap
41+
expect(isOrdered(col)).toBe(constructorIsOrdered);
42+
expect(isOrdered(grouped)).toBe(constructorIsOrdered);
43+
if (constructorIsOrdered) {
44+
expect(grouped).toBeInstanceOf(OrderedMap);
45+
} else {
46+
expect(grouped).not.toBeInstanceOf(OrderedMap);
47+
}
48+
}
49+
);
50+
451
it('groups keyed sequence', () => {
552
const grouped = Seq({ a: 1, b: 2, c: 3, d: 4 }).groupBy(x => x % 2);
653
expect(grouped.toJS()).toEqual({ 1: { a: 1, c: 3 }, 0: { b: 2, d: 4 } });
@@ -14,53 +61,38 @@ describe('groupBy', () => {
1461
});
1562

1663
it('groups indexed sequence', () => {
17-
expect(
18-
Seq([1, 2, 3, 4, 5, 6])
19-
.groupBy(x => x % 2)
20-
.toJS()
21-
).toEqual({ 1: [1, 3, 5], 0: [2, 4, 6] });
64+
const group = Seq([1, 2, 3, 4, 5, 6]).groupBy(x => x % 2);
65+
66+
expect(group.toJS()).toEqual({ 1: [1, 3, 5], 0: [2, 4, 6] });
2267
});
2368

2469
it('groups to keys', () => {
25-
expect(
26-
Seq([1, 2, 3, 4, 5, 6])
27-
.groupBy(x => (x % 2 ? 'odd' : 'even'))
28-
.toJS()
29-
).toEqual({ odd: [1, 3, 5], even: [2, 4, 6] });
70+
const group = Seq([1, 2, 3, 4, 5, 6]).groupBy(x =>
71+
x % 2 ? 'odd' : 'even'
72+
);
73+
expect(group.toJS()).toEqual({ odd: [1, 3, 5], even: [2, 4, 6] });
3074
});
3175

3276
it('groups indexed sequences, maintaining indicies when keyed sequences', () => {
33-
expect(
34-
Seq([1, 2, 3, 4, 5, 6])
35-
.groupBy(x => x % 2)
36-
.toJS()
37-
).toEqual({ 1: [1, 3, 5], 0: [2, 4, 6] });
38-
expect(
39-
Seq([1, 2, 3, 4, 5, 6])
40-
.toKeyedSeq()
41-
.groupBy(x => x % 2)
42-
.toJS()
43-
).toEqual({ 1: { 0: 1, 2: 3, 4: 5 }, 0: { 1: 2, 3: 4, 5: 6 } });
44-
});
77+
const group = Seq([1, 2, 3, 4, 5, 6]).groupBy(x => x % 2);
4578

46-
it('has groups that can be mapped', () => {
47-
expect(
48-
Seq([1, 2, 3, 4, 5, 6])
49-
.groupBy(x => x % 2)
50-
.map(group => group.map(value => value * 10))
51-
.toJS()
52-
).toEqual({ 1: [10, 30, 50], 0: [20, 40, 60] });
79+
expect(group.toJS()).toEqual({ 1: [1, 3, 5], 0: [2, 4, 6] });
80+
81+
const keyedGroup = Seq([1, 2, 3, 4, 5, 6])
82+
.toKeyedSeq()
83+
.groupBy(x => x % 2);
84+
85+
expect(keyedGroup.toJS()).toEqual({
86+
1: { 0: 1, 2: 3, 4: 5 },
87+
0: { 1: 2, 3: 4, 5: 6 },
88+
});
5389
});
5490

55-
it('returns an ordered map from an ordered collection', () => {
56-
const seq = Seq(['Z', 'Y', 'X', 'Z', 'Y', 'X']);
57-
expect(Collection.isOrdered(seq)).toBe(true);
58-
const seqGroups = seq.groupBy(x => x);
59-
expect(Collection.isOrdered(seqGroups)).toBe(true);
91+
it('has groups that can be mapped', () => {
92+
const mappedGroup = Seq([1, 2, 3, 4, 5, 6])
93+
.groupBy(x => x % 2)
94+
.map(group => group.map(value => value * 10));
6095

61-
const map = Map({ x: 1, y: 2 });
62-
expect(Collection.isOrdered(map)).toBe(false);
63-
const mapGroups = map.groupBy(x => x);
64-
expect(Collection.isOrdered(mapGroups)).toBe(false);
96+
expect(mappedGroup.toJS()).toEqual({ 1: [10, 30, 50], 0: [20, 40, 60] });
6597
});
6698
});

0 commit comments

Comments
 (0)