sendou.ink/app/utils/arrays.ts
Kalle 7dec8c572e
SendouQ Season 2 changes (#1542)
* Initial

* Saves preferences

* Include TW

* mapModePreferencesToModeList

* mapPoolFromPreferences initial

* Preference to map pool

* Adjust seed

* q.looking tests

* adds about created map preferences to memento in the correct spot (two preferrers)

* Failing test about modes

* Mode preferences to memento

* Remove old Plus Voting code

* Fix seeding

* find match by id via kysely

* View map memento

* Fix up map list generation logic

* Mode memento info

* Future match modes

* Add TODO

* Migration number

* Migrate test DB

* Remove old map pool code

* createGroupFromPrevious new

* Settings styling

* VC to settings

* Weapon pool

* Add TODOs

* Progress

* Adjust mode exclusion policy

* Progress

* Progress

* Progress

* Notes in progress

* Note feedback after submit

* Textarea styling

* Unskip tests

* Note sorting failing test

* Private note in Q

* Ownerpicksmaps later

* New bottom section

* Mobile layout initial

* Add basic match meta

* Tabs initial

* Sticky tab

* Unseen messages in match page

* Front page i18n

* Settings i18n

* Looking 18n

* Chat i18n

* Progress

* Tranfer weapon pools script

* Sticky on match page

* Match page translations

* i18n - tiers page

* Preparing page i18n

* Icon

* Show add note right after report
2023-11-30 20:57:06 +02:00

87 lines
2.4 KiB
TypeScript

// TODO: when more examples of permissions profile difference between
// this implementation and one that takes arrays
import clone from "just-clone";
import shuffle from "just-shuffle";
import invariant from "tiny-invariant";
// (not all arrays need to necessarily run but they need to be defined)
export function allTruthy(arr: unknown[]) {
return arr.every(Boolean);
}
/** Mimics Array.prototype.at except throws an error if index out of bounds */
export function atOrError<T>(arr: T[], n: number) {
const result = at(arr, n);
if (result === undefined) {
throw new Error(`Index ${n} out of bounds. Array length is ${arr.length}`);
}
return result;
}
// https://github.com/tc39/proposal-relative-indexing-method#polyfill
/** Array.at polyfill */
function at<T>(arr: T[], n: number) {
// ToInteger() abstract op
n = Math.trunc(n) || 0;
// Allow negative indexing from the end
if (n < 0) n += arr.length;
// OOB access is guaranteed to return undefined
if (n < 0 || n >= arr.length) return undefined;
// Otherwise, this is just normal property access
return arr[n];
}
// TODO: i18n (at least for SendouQ)
export function joinListToNaturalString(arg: string[], lastSeparator = "and") {
if (arg.length === 1) return arg[0];
const list = [...arg];
const last = list.pop();
const commaJoined = list.join(", ");
return last ? `${commaJoined} ${lastSeparator} ${last}` : commaJoined;
}
export function normalizeFormFieldArray(
value: undefined | null | string | string[],
): string[] {
return value == null ? [] : typeof value === "string" ? [value] : value;
}
/** Can be used as a strongly typed array filter */
export function isDefined<T>(value: T | undefined | null): value is T {
return value !== null && value !== undefined;
}
export function removeDuplicates<T>(arr: T[]): T[] {
return [...new Set(arr)];
}
export function removeDuplicatesByProperty<T>(
arr: T[],
getter: (arg0: T) => number | string,
): T[] {
const seen = new Set();
return arr.filter((item) => {
const id = getter(item);
if (seen.has(id)) return false;
seen.add(id);
return true;
});
}
export function nullFilledArray(size: number) {
return new Array(size).fill(null);
}
export function pickRandomItem<T>(array: T[]): T {
invariant(array.length > 0, "Can't pick from empty array");
const shuffled = shuffle(clone(array));
return shuffled[0];
}