Skip to content

Commit f8b46d0

Browse files
authored
feat(collections): implement associateBy (#1071)
1 parent bffc4ce commit f8b46d0

File tree

2 files changed

+165
-0
lines changed

2 files changed

+165
-0
lines changed

collections/associate_by.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
2+
3+
/**
4+
* Transforms the given array into a Record, extracting the key of each element using the given selector.
5+
* If the selector produces the same key for multiple elements, the latest one will be used (overriding the
6+
* ones before it).
7+
*
8+
* Example:
9+
*
10+
* ```ts
11+
* import { associateBy } from "./associate_by.ts"
12+
*
13+
* const users = [
14+
* { id: 'a2e', userName: 'Anna' },
15+
* { id: '5f8', userName: 'Arnold' },
16+
* { id: 'd2c', userName: 'Kim' },
17+
* ]
18+
* const usersById = associateBy(users, it => it.id)
19+
*
20+
* console.assert(usersById === {
21+
* 'a2e': { id: 'a2e', userName: 'Anna' },
22+
* '5f8': { id: '5f8', userName: 'Arnold' },
23+
* 'd2c': { id: 'd2c', userName: 'Kim' },
24+
* })
25+
* ```
26+
*/
27+
export function associateBy<T>(
28+
array: Array<T>,
29+
selector: (el: T) => string,
30+
): Record<string, T> {
31+
const ret: Record<string, T> = {};
32+
33+
for (const element of array) {
34+
const selectedValue = selector(element);
35+
36+
ret[selectedValue] = element;
37+
}
38+
39+
return ret;
40+
}

collections/associate_by_test.ts

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
2+
3+
import { assertEquals } from "../testing/asserts.ts";
4+
import { associateBy } from "./associate_by.ts";
5+
6+
function associateByTest<T>(
7+
input: [Array<T>, (el: T) => string],
8+
expected: { [x: string]: T },
9+
message?: string,
10+
) {
11+
const actual = associateBy(...input);
12+
assertEquals(actual, expected, message);
13+
}
14+
15+
Deno.test({
16+
name: "[collections/associateBy] no mutation",
17+
fn() {
18+
const arrayA = ["Foo", "Bar"];
19+
associateBy(arrayA, (it) => it.charAt(0));
20+
21+
assertEquals(arrayA, ["Foo", "Bar"]);
22+
},
23+
});
24+
25+
Deno.test({
26+
name: "[collections/associateBy] empty input",
27+
fn() {
28+
associateByTest(
29+
[[], () => "a"],
30+
{},
31+
);
32+
},
33+
});
34+
35+
Deno.test({
36+
name: "[collections/associateBy] constant key",
37+
fn() {
38+
associateByTest(
39+
[[1, 3, 5, 6], () => "a"],
40+
{ a: 6 },
41+
);
42+
},
43+
});
44+
45+
Deno.test({
46+
name: "[collections/associateBy] empty key",
47+
fn() {
48+
associateByTest(
49+
[
50+
["Foo", "b"],
51+
(it) => it.charAt(1),
52+
],
53+
{
54+
"o": "Foo",
55+
"": "b",
56+
},
57+
);
58+
},
59+
});
60+
61+
Deno.test({
62+
name: "[collections/associateBy] duplicate keys",
63+
fn() {
64+
associateByTest(
65+
[
66+
["Anna", "Marija", "Karl", "Arnold", "Martha"],
67+
(it) => it.charAt(0),
68+
],
69+
{
70+
"A": "Arnold",
71+
"M": "Martha",
72+
"K": "Karl",
73+
},
74+
);
75+
associateByTest(
76+
[
77+
[1.2, 2, 2.3, 6.3, 6.9, 6],
78+
(it) => Math.floor(it).toString(),
79+
],
80+
{
81+
"1": 1.2,
82+
"2": 2.3,
83+
"6": 6,
84+
},
85+
);
86+
},
87+
});
88+
89+
Deno.test({
90+
name: "[collections/associateBy] associates",
91+
fn() {
92+
associateByTest(
93+
[
94+
[
95+
{ name: "test", done: false },
96+
{ name: "build", done: true },
97+
{ name: "deploy", done: false },
98+
{ name: "audit", done: true },
99+
],
100+
(it) => it.name,
101+
],
102+
{
103+
"test": { name: "test", done: false },
104+
"build": { name: "build", done: true },
105+
"deploy": { name: "deploy", done: false },
106+
"audit": { name: "audit", done: true },
107+
},
108+
);
109+
associateByTest(
110+
[
111+
[
112+
113+
114+
115+
],
116+
(it) => it.substring(0, it.indexOf("@")),
117+
],
118+
{
119+
"anna": "[email protected]",
120+
"josh": "[email protected]",
121+
122+
},
123+
);
124+
},
125+
});

0 commit comments

Comments
 (0)