Convert `checksRunsSelected` state from array to set

Release-Notes: skip
Change-Id: Icaf33559fe3b4e924a2514ed8debcabbc95a8204
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-results.ts b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
index 4530631..848948f 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-results.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
@@ -768,7 +768,7 @@
    * is empty, then no run is selected and all runs should be shown.
    */
   @state()
-  selectedRuns: string[] = [];
+  selectedRuns: Set<string> = new Set();
 
   @state()
   actions: Action[] = [];
@@ -1304,10 +1304,7 @@
   }
 
   isRunSelected(run: {checkName: string}) {
-    return (
-      this.selectedRuns.length === 0 ||
-      this.selectedRuns.includes(run.checkName)
-    );
+    return this.selectedRuns.size === 0 || this.selectedRuns.has(run.checkName);
   }
 
   renderFilter() {
@@ -1316,7 +1313,7 @@
         this.isRunSelected(run) && isAttemptSelected(this.selectedAttempt, run)
     );
     if (
-      this.selectedRuns.length === 0 &&
+      this.selectedRuns.size === 0 &&
       allResults(runs).length <= 3 &&
       this.filterRegExp === ''
     ) {
@@ -1360,7 +1357,7 @@
       ],
       []
     );
-    const isSelectionActive = this.selectedRuns.length > 0;
+    const isSelectionActive = this.selectedRuns.size > 0;
     const selected = all.filter(result => this.isRunSelected(result));
     const re = new RegExp(this.filterRegExp, 'i');
     const filtered = selected.filter(result => matches(result, re));
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-runs.ts b/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
index b068426..128a9b0a 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
@@ -405,7 +405,7 @@
   collapsed = false;
 
   @state()
-  selectedRuns: string[] = [];
+  selectedRuns: Set<string> = new Set();
 
   @state()
   selectedAttempt: AttemptChoice = LATEST_ATTEMPT;
@@ -668,8 +668,8 @@
 
   private renderTitleButtons() {
     if (this.collapsed) return;
-    if (this.selectedRuns.length < 2) return;
-    const actions = this.selectedRuns.map(selected => {
+    if (this.selectedRuns.size < 2) return;
+    const actions = [...this.selectedRuns].map(selected => {
       const run = this.runs.find(
         run => run.isLatestAttempt && run.checkName === selected
       );
@@ -685,7 +685,7 @@
         class="font-normal"
         link
         @click=${() =>
-          this.getViewModel().updateState({checksRunsSelected: []})}
+          this.getViewModel().updateState({checksRunsSelected: undefined})}
         >Unselect All</gr-button
       >
       <gr-tooltip-content
@@ -829,8 +829,8 @@
   }
 
   renderRun(run: CheckRun) {
-    const selectedRun = this.selectedRuns.includes(run.checkName);
-    const deselected = !selectedRun && this.selectedRuns.length > 0;
+    const selectedRun = this.selectedRuns.has(run.checkName);
+    const deselected = !selectedRun && this.selectedRuns.size > 0;
     return html`<gr-checks-run
       .run=${run}
       ?condensed=${this.collapsed}
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
index 0c9b6302..f829313 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
@@ -1411,7 +1411,7 @@
     const attempt = stringToAttemptChoice(queryMap.get('attempt'));
     if (attempt && attempt !== LATEST_ATTEMPT) state.attempt = attempt;
     const selected = queryMap.get('checksRunsSelected');
-    if (selected) state.checksRunsSelected = selected.split(',');
+    if (selected) state.checksRunsSelected = new Set(selected.split(','));
 
     assertIsDefined(state.project, 'project');
     this.reporting.setRepoName(state.project);
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.ts b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.ts
index 5c5e67d..119ba48 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.ts
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.ts
@@ -1161,7 +1161,7 @@
             attempt: 1,
             filter: 'fff',
             tab: 'checks',
-            checksRunsSelected: ['asdf', 'qwer'],
+            checksRunsSelected: new Set(['asdf', 'qwer']),
             checksResultsFilter: 'asdf.*qwer',
           });
         });
diff --git a/polygerrit-ui/app/models/views/change.ts b/polygerrit-ui/app/models/views/change.ts
index 6b80f23..3790bb0 100644
--- a/polygerrit-ui/app/models/views/change.ts
+++ b/polygerrit-ui/app/models/views/change.ts
@@ -14,7 +14,7 @@
 import {Tab} from '../../constants/constants';
 import {GerritView} from '../../services/router/router-model';
 import {UrlEncodedCommentId} from '../../types/common';
-import {toggle} from '../../utils/common-util';
+import {toggleSet} from '../../utils/common-util';
 import {select} from '../../utils/observable-util';
 import {
   encodeURL,
@@ -47,7 +47,7 @@
   /** selected attempt for check runs (undefined=latest) */
   attempt?: AttemptChoice;
   /** selected check runs identified by `checkName` */
-  checksRunsSelected?: string[];
+  checksRunsSelected?: Set<string>;
   /** regular expression for filtering check results */
   checksResultsFilter?: string;
 
@@ -117,7 +117,7 @@
   if (state.checksResultsFilter) {
     queries.push(`checksResultsFilter=${state.checksResultsFilter}`);
   }
-  if (state.checksRunsSelected && state.checksRunsSelected.length > 0) {
+  if (state.checksRunsSelected && state.checksRunsSelected.size > 0) {
     queries.push(`checksRunsSelected=${[...state.checksRunsSelected].sort()}`);
   }
   if (state.tab && state.tab !== Tab.FILES) {
@@ -174,7 +174,7 @@
 
   public readonly checksRunsSelected$ = select(
     this.state$,
-    state => state?.checksRunsSelected ?? []
+    state => state?.checksRunsSelected ?? new Set<string>()
   );
 
   constructor() {
@@ -191,7 +191,9 @@
   }
 
   toggleSelectedCheckRun(checkName: string) {
-    const selected = this.getState()?.checksRunsSelected ?? [];
-    this.updateState({checksRunsSelected: toggle(selected, checkName)});
+    const current = this.getState()?.checksRunsSelected ?? new Set();
+    const next = new Set(current);
+    toggleSet(next, checkName);
+    this.updateState({checksRunsSelected: next});
   }
 }
diff --git a/polygerrit-ui/app/models/views/change_test.ts b/polygerrit-ui/app/models/views/change_test.ts
index f756d4b..2cea325 100644
--- a/polygerrit-ui/app/models/views/change_test.ts
+++ b/polygerrit-ui/app/models/views/change_test.ts
@@ -39,7 +39,7 @@
   test('createChangeUrl() checksRunsSelected', () => {
     const state: ChangeViewState = {
       ...STATE,
-      checksRunsSelected: ['asdf'],
+      checksRunsSelected: new Set(['asdf']),
     };
 
     assert.equal(
diff --git a/polygerrit-ui/app/utils/common-util.ts b/polygerrit-ui/app/utils/common-util.ts
index 03306f5..183d167 100644
--- a/polygerrit-ui/app/utils/common-util.ts
+++ b/polygerrit-ui/app/utils/common-util.ts
@@ -117,7 +117,7 @@
 /**
  * Add value, if the set does not contain it. Otherwise remove it.
  */
-export function toggleSetMembership<T>(set: Set<T>, value: T): void {
+export function toggleSet<T>(set: Set<T>, value: T): void {
   if (set.has(value)) {
     set.delete(value);
   } else {
@@ -125,10 +125,6 @@
   }
 }
 
-export function unique<T>(item: T, index: number, array: T[]) {
-  return array.indexOf(item) === index;
-}
-
 export function toggle<T>(array: T[], item: T): T[] {
   if (array.includes(item)) {
     return array.filter(r => r !== item);
@@ -137,6 +133,10 @@
   }
 }
 
+export function unique<T>(item: T, index: number, array: T[]) {
+  return array.indexOf(item) === index;
+}
+
 /**
  * Returns the elements that are present in every sub-array. If a compareBy
  * predicate is passed in, it will be used instead of strict equality. A new