Skip to content

Commit 90c3211

Browse files
committed
Return a Map from groupBy/keyBy
I'd like to remove the `__proto__: empty` hacks which are buggy (see Flow issue 8685). We're currently returning objects with null prototypes from groupBy/keyBy, because these objects are meant to be used as maps. So, we should really just be using maps. I made that change, which actually simplifies some of the usages.
1 parent f07edd4 commit 90c3211

File tree

13 files changed

+99
-98
lines changed

13 files changed

+99
-98
lines changed

root/components/list/ReleaseGroupList.js

+24-25
Original file line numberDiff line numberDiff line change
@@ -153,31 +153,30 @@ const ReleaseGroupList = ({
153153
sortable,
154154
}: ReleaseGroupListProps): Array<React$Node> => {
155155
const groupedReleaseGroups = groupBy(releaseGroups, x => x.typeName ?? '');
156-
return (
157-
Object.keys(groupedReleaseGroups).map<React$Node>((type) => {
158-
const releaseGroupsOfType = groupedReleaseGroups[type];
159-
return (
160-
<React.Fragment key={type}>
161-
<h3>
162-
{type === ''
163-
? l('Unspecified type')
164-
: releaseGroupType(releaseGroupsOfType[0])
165-
}
166-
</h3>
167-
<ReleaseGroupListTable
168-
checkboxes={checkboxes}
169-
mergeForm={mergeForm}
170-
order={order}
171-
releaseGroups={releaseGroupsOfType}
172-
seriesItemNumbers={seriesItemNumbers}
173-
showRatings={showRatings}
174-
showType={false}
175-
sortable={sortable}
176-
/>
177-
</React.Fragment>
178-
);
179-
})
180-
);
156+
const tables = [];
157+
for (const [type, releaseGroupsOfType] of groupedReleaseGroups) {
158+
tables.push(
159+
<React.Fragment key={type}>
160+
<h3>
161+
{type === ''
162+
? l('Unspecified type')
163+
: releaseGroupType(releaseGroupsOfType[0])
164+
}
165+
</h3>
166+
<ReleaseGroupListTable
167+
checkboxes={checkboxes}
168+
mergeForm={mergeForm}
169+
order={order}
170+
releaseGroups={releaseGroupsOfType}
171+
seriesItemNumbers={seriesItemNumbers}
172+
showRatings={showRatings}
173+
showType={false}
174+
sortable={sortable}
175+
/>
176+
</React.Fragment>,
177+
);
178+
}
179+
return tables;
181180
};
182181

183182
export default ReleaseGroupList;

root/static/scripts/common/MB/Control/Autocomplete.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,7 @@ MB.Control.autocomplete_formatters = {
763763
if (item.labels) {
764764
for (
765765
const [name, releaseLabels] of
766-
Object.entries(groupBy(item.labels, getLabelName))
766+
groupBy(item.labels, getLabelName)
767767
) {
768768
const catalogNumbers =
769769
compactMap(releaseLabels, getCatalogNumber)

root/static/scripts/common/components/TagEditor.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ function createInitialTagState(
685685
const used = new Set();
686686

687687
const combined = aggregatedTags.map(function (t) {
688-
const userTag = userTagsByName[t.tag.name];
688+
const userTag = userTagsByName.get(t.tag.name);
689689

690690
used.add(t.tag.name);
691691

@@ -697,8 +697,7 @@ function createInitialTagState(
697697
});
698698

699699
// Always show upvoted user tags (affects sidebar)
700-
for (const tagName of Object.keys(userTagsByName)) {
701-
const tag = userTagsByName[tagName];
700+
for (const [tagName, tag] of userTagsByName) {
702701
if (tag.vote > 0 && !used.has(tagName)) {
703702
combined.push(tag);
704703
}

root/static/scripts/common/utility/arrays.js

+14-12
Original file line numberDiff line numberDiff line change
@@ -130,32 +130,34 @@ export function sortByString<T>(
130130
return keys.map(x => array[x[0]]);
131131
}
132132

133-
export function groupBy<T>(
133+
export function groupBy<T, K>(
134134
array: $ReadOnlyArray<T>,
135-
func: (T) => string,
136-
): {__proto__: empty, [groupKey: string]: $ReadOnlyArray<T>} {
135+
func: (T) => K,
136+
): Map<K, Array<T>> {
137137
return array.reduce(function (result, item) {
138138
const key = func(item);
139-
if (!(key in result)) {
140-
result[key] = [];
139+
let values = result.get(key);
140+
if (values == null) {
141+
values = [];
142+
result.set(key, values);
141143
}
142-
result[key].push(item);
144+
values.push(item);
143145
return result;
144-
}, Object.create(null));
146+
}, new Map());
145147
}
146148

147149
export function first<T>(array: ?$ReadOnlyArray<T>): ?T {
148150
return array?.length ? array[0] : undefined;
149151
}
150152

151-
export function keyBy<T>(
153+
export function keyBy<T, K = string>(
152154
array: $ReadOnlyArray<T>,
153-
func: (T) => string,
154-
): {__proto__: empty, [groupKey: string]: T} {
155+
func: (T) => K,
156+
): Map<K, T> {
155157
return array.reduce(function (result, item) {
156-
result[func(item)] = item;
158+
result.set(func(item), item);
157159
return result;
158-
}, Object.create(null));
160+
}, new Map());
159161
}
160162

161163
export function last<T>(array: ?$ReadOnlyArray<T>): ?T {

root/static/scripts/edit/components/edit/RelationshipDiff.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ const RelationshipDiff = ({
5858
const typeId = String(attr.typeID);
5959
const display = displayLinkAttribute(attr);
6060

61-
if (oldAttrs[typeId] && !newAttrs[typeId]) {
61+
if (oldAttrs.has(typeId) && !newAttrs.get(typeId)) {
6262
return diffOnlyA(display);
6363
}
6464

65-
if (newAttrs[typeId] && !oldAttrs[typeId]) {
65+
if (newAttrs.get(typeId) && !oldAttrs.has(typeId)) {
6666
return diffOnlyB(display);
6767
}
6868

root/static/scripts/edit/components/edit/ReleaseEventsDiff.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -81,37 +81,37 @@ const ReleaseEventsDiff = ({
8181
const oldEventsByCountry = keyBy(oldEvents, getCountryId);
8282
const newEventsByCountry = keyBy(newEvents, getCountryId);
8383

84-
const oldKeys = Object.keys(oldEventsByCountry).sort();
85-
const newKeys = Object.keys(newEventsByCountry).sort();
84+
const oldKeys = Array.from(oldEventsByCountry.keys()).sort();
85+
const newKeys = Array.from(newEventsByCountry.keys()).sort();
8686

8787
const oldSide = [];
8888
const newSide = [];
8989

9090
for (let i = 0; i < oldKeys.length; i++) {
9191
const key = oldKeys[i];
92-
const oldEvent = oldEventsByCountry[key];
93-
let newEvent = newEventsByCountry[key];
92+
const oldEvent = oldEventsByCountry.get(key);
93+
let newEvent = newEventsByCountry.get(key);
9494
/*
9595
* If this country was removed, compare against the new entry at
9696
* the same position visually.
9797
*/
9898
if (!newEvent && i < newKeys.length) {
99-
newEvent = newEventsByCountry[newKeys[i]];
99+
newEvent = newEventsByCountry.get(newKeys[i]);
100100
}
101101
oldSide.push(changeSide(oldEvent, newEvent, DELETE));
102102
}
103103

104104
for (let i = 0; i < newKeys.length; i++) {
105105
const key = newKeys[i];
106-
let oldEvent = oldEventsByCountry[key];
106+
let oldEvent = oldEventsByCountry.get(key);
107107
/*
108108
* If this country was added, compare against the old entry at
109109
* the same position visually.
110110
*/
111111
if (!oldEvent && i < oldKeys.length) {
112-
oldEvent = oldEventsByCountry[oldKeys[i]];
112+
oldEvent = oldEventsByCountry.get(oldKeys[i]);
113113
}
114-
const newEvent = newEventsByCountry[key];
114+
const newEvent = newEventsByCountry.get(key);
115115
newSide.push(changeSide(oldEvent, newEvent, INSERT));
116116
}
117117

root/static/scripts/edit/externalLinks.js

+20-24
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,15 @@ import * as URLCleanup from './URLCleanup';
3636
import validation from './validation';
3737

3838
type LinkStateT = {
39-
relationship: number | string | null,
39+
// New relationships will use a unique string ID like "new-1".
40+
relationship: StrOrNum | null,
4041
type: number | null,
4142
url: string,
4243
video: boolean,
4344
...
4445
};
4546

46-
type LinkHashT = {
47-
__proto__: empty,
48-
+[key: number | string | null]: LinkStateT,
49-
...
50-
};
47+
type LinkMapT = Map<string, LinkStateT>;
5148

5249
type LinksEditorProps = {
5350
errorObservable: (boolean) => void,
@@ -135,27 +132,27 @@ export class ExternalLinksEditor
135132
});
136133
}
137134

138-
getOldLinksHash(): LinkHashT {
139-
return keyBy(
135+
getOldLinksHash(): LinkMapT {
136+
return keyBy<LinkStateT, string>(
140137
this.props.initialLinks
141138
.filter(link => isPositiveInteger(link.relationship)),
142139
x => String(x.relationship),
143140
);
144141
}
145142

146143
getEditData(): {
147-
allLinks: LinkHashT,
148-
newLinks: LinkHashT,
149-
oldLinks: LinkHashT,
144+
allLinks: LinkMapT,
145+
newLinks: LinkMapT,
146+
oldLinks: LinkMapT,
150147
} {
151148
const oldLinks = this.getOldLinksHash();
152-
const newLinks = keyBy<
153-
LinkStateT,
154-
$ElementType<LinkStateT, 'relationship'>,
155-
>(this.state.links, x => String(x.relationship));
149+
const newLinks: LinkMapT = keyBy(
150+
this.state.links,
151+
x => String(x.relationship),
152+
);
156153

157154
return {
158-
allLinks: {...oldLinks, ...newLinks},
155+
allLinks: new Map([...oldLinks, ...newLinks]),
159156
newLinks: newLinks,
160157
oldLinks: oldLinks,
161158
};
@@ -170,10 +167,7 @@ export class ExternalLinksEditor
170167
const backward = this.props.sourceType > 'url';
171168
const {oldLinks, newLinks, allLinks} = this.getEditData();
172169

173-
for (
174-
const [relationship, link] of
175-
((Object.entries(allLinks): any): $ReadOnlyArray<[string, ?LinkStateT]>)
176-
) {
170+
for (const [relationship, link] of allLinks) {
177171
if (!link?.type) {
178172
return;
179173
}
@@ -183,7 +177,7 @@ export class ExternalLinksEditor
183177
if (isPositiveInteger(relationship)) {
184178
pushInput(prefix, 'relationship_id', String(relationship));
185179

186-
if (!newLinks[relationship]) {
180+
if (!newLinks.has(relationship)) {
187181
pushInput(prefix, 'removed', '1');
188182
}
189183
}
@@ -192,7 +186,7 @@ export class ExternalLinksEditor
192186

193187
if (link.video) {
194188
pushInput(prefix + '.attributes.0', 'type.gid', VIDEO_ATTRIBUTE_GID);
195-
} else if ((oldLinks[relationship] || {}).video) {
189+
} else if (oldLinks.get(relationship)?.video) {
196190
pushInput(prefix + '.attributes.0', 'type.gid', VIDEO_ATTRIBUTE_GID);
197191
pushInput(prefix + '.attributes.0', 'removed', '1');
198192
}
@@ -231,7 +225,7 @@ export class ExternalLinksEditor
231225
const linkType = link.type
232226
? linkedEntities.link_type[link.type] : {};
233227
const checker = URLCleanup.validationRules[linkType.gid];
234-
const oldLink = oldLinks[link.relationship];
228+
const oldLink = oldLinks.get(String(link.relationship));
235229
const isNewLink = !isPositiveInteger(link.relationship);
236230
const linkChanged = oldLink && link.url !== oldLink.url;
237231
const isNewOrChangedLink = (isNewLink || linkChanged);
@@ -274,7 +268,9 @@ export class ExternalLinksEditor
274268
and should not be used.`);
275269
errorTarget = URLCleanup.ERROR_TARGETS.RELATIONSHIP;
276270
} else if (
277-
(linksByTypeAndUrl[linkTypeAndUrlString(link)] || []).length > 1
271+
(linksByTypeAndUrl.get(
272+
linkTypeAndUrlString(link),
273+
) || []).length > 1
278274
) {
279275
error = l('This relationship already exists.');
280276
errorTarget = URLCleanup.ERROR_TARGETS.RELATIONSHIP;

root/static/scripts/relationship-editor/common/dialog.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,7 @@ function splitByCreditableAttributes(relationship) {
912912

913913
for (
914914
const attributes of
915-
Object.values(groupBy(creditable, linkAttributeTypeID))
915+
groupBy(creditable, linkAttributeTypeID).values()
916916
) {
917917
const extra = attributes.slice(1);
918918
relationship.attributes.removeAll(extra);

root/static/scripts/relationship-editor/common/viewModel.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ RE.exportTypeInfo = function (typeInfo, attrInfo) {
5656
}
5757
switch (item.entityType) {
5858
case 'link_attribute_type':
59-
const children = attrChildren[item.id];
59+
const children = attrChildren.get(item.id);
6060
if (children) {
6161
item.children = children;
6262
}

root/static/scripts/release-editor/edits.js

+14-10
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,10 @@ releaseEditor.edits = {
114114
var edits = [];
115115

116116
for (let newLabel of newLabels) {
117-
const id = newLabel.release_label;
117+
const id = getReleaseLabel(newLabel);
118118

119119
if (id) {
120-
const oldLabel = oldLabelsByID[id];
120+
const oldLabel = oldLabelsByID.get(id);
121121

122122
if (oldLabel && !deepEqual(newLabel, oldLabel)) {
123123
// Edit ReleaseLabel
@@ -135,8 +135,8 @@ releaseEditor.edits = {
135135
}
136136

137137
for (let oldLabel of oldLabels) {
138-
const id = oldLabel.release_label;
139-
const newLabel = newLabelsByID[id];
138+
const id = getReleaseLabel(oldLabel);
139+
const newLabel = newLabelsByID.get(id);
140140

141141
if (!newLabel || !(newLabel.label || newLabel.catalog_number)) {
142142
// Delete ReleaseLabel
@@ -383,19 +383,21 @@ releaseEditor.edits = {
383383
return edits;
384384
}
385385

386-
for (const link of Object.values(allLinks)) {
386+
for (const link of allLinks.values()) {
387387
if (!link.type || !link.url) {
388388
continue;
389389
}
390390

391391
const newData = MB.edit.fields.externalLinkRelationship(link, release);
392+
const relationshipId = link.relationship;
393+
const relationshipIdString = String(relationshipId);
392394

393395
if (isPositiveInteger(link.relationship)) {
394-
if (!newLinks[link.relationship]) {
396+
if (!newLinks.has(relationshipIdString)) {
395397
edits.push(MB.edit.relationshipDelete(newData));
396-
} else if (oldLinks[link.relationship]) {
398+
} else if (oldLinks.has(relationshipIdString)) {
397399
const original = MB.edit.fields.externalLinkRelationship(
398-
oldLinks[link.relationship],
400+
oldLinks.get(relationshipIdString),
399401
release,
400402
);
401403

@@ -412,7 +414,7 @@ releaseEditor.edits = {
412414
edits.push(editData);
413415
}
414416
}
415-
} else if (newLinks[link.relationship]) {
417+
} else if (newLinks.has(relationshipIdString)) {
416418
edits.push(MB.edit.relationshipCreate(newData));
417419
}
418420
}
@@ -667,7 +669,9 @@ releaseEditor.orderedEditSubmissions = [
667669
var newMediums = release.mediums();
668670

669671
newMediums.filter(m => m.id == null).forEach(function (medium) {
670-
var addedData = added[medium.tmpPosition || medium.position()];
672+
var addedData = added.get(
673+
String(medium.tmpPosition || medium.position()),
674+
);
671675

672676
if (addedData) {
673677
medium.id = addedData.id;

0 commit comments

Comments
 (0)