- {removeDuplicates(
+ {R.unique(
team.members
.map((member) => member.country)
.filter((country) => country !== null),
diff --git a/app/features/top-search/loaders/xsearch.player.$id.server.ts b/app/features/top-search/loaders/xsearch.player.$id.server.ts
index 50897bc00..19e9cb781 100644
--- a/app/features/top-search/loaders/xsearch.player.$id.server.ts
+++ b/app/features/top-search/loaders/xsearch.player.$id.server.ts
@@ -1,5 +1,5 @@
import type { LoaderFunctionArgs } from "@remix-run/node";
-import { removeDuplicates } from "~/utils/arrays";
+import * as R from "remeda";
import { notFoundIfFalsy, parseParams } from "~/utils/remix.server";
import { idObject } from "~/utils/zod";
import { findPlacementsByPlayerId } from "../queries/findPlacements.server";
@@ -13,7 +13,7 @@ export const loader = async (args: LoaderFunctionArgs) => {
const placements = notFoundIfFalsy(findPlacementsByPlayerId(params.id));
const primaryName = placements[0].name;
- const aliases = removeDuplicates(
+ const aliases = R.unique(
placements
.map((placement) => placement.name)
.filter((name) => name !== primaryName),
diff --git a/app/features/tournament-bracket/core/Bracket.test.ts b/app/features/tournament-bracket/core/Bracket.test.ts
index e5cb4af05..795a067e4 100644
--- a/app/features/tournament-bracket/core/Bracket.test.ts
+++ b/app/features/tournament-bracket/core/Bracket.test.ts
@@ -1,5 +1,5 @@
+import * as R from "remeda";
import { describe, expect, it } from "vitest";
-import { removeDuplicates } from "../../../utils/arrays";
import invariant from "../../../utils/invariant";
import { Tournament } from "./Tournament";
import { PADDLING_POOL_255 } from "./tests/mocks";
@@ -47,9 +47,7 @@ describe("round robin standings", () => {
const standings = tournamentPP255.bracketByIdx(0)!.standings;
- const groupIds = removeDuplicates(
- standings.map((standing) => standing.groupId),
- );
+ const groupIds = R.unique(standings.map((standing) => standing.groupId));
expect(
groupIds.length,
"Paddling Pool 255 should have groups from Group A to Group I",
@@ -81,7 +79,7 @@ describe("round robin standings", () => {
const standings = tournamentPP255.bracketByIdx(0)!.standings;
- const placements = removeDuplicates(
+ const placements = R.unique(
standings.map((standing) => standing.placement),
).sort((a, b) => a - b);
diff --git a/app/features/tournament-bracket/core/Bracket.ts b/app/features/tournament-bracket/core/Bracket.ts
index 627ced2e6..0c1e62a65 100644
--- a/app/features/tournament-bracket/core/Bracket.ts
+++ b/app/features/tournament-bracket/core/Bracket.ts
@@ -1,9 +1,9 @@
import { sub } from "date-fns";
+import * as R from "remeda";
import type { Tables, TournamentStageSettings } from "~/db/tables";
import { TOURNAMENT } from "~/features/tournament";
import type { TournamentManagerDataSet } from "~/modules/brackets-manager/types";
import type { Round } from "~/modules/brackets-model";
-import { removeDuplicates } from "~/utils/arrays";
import invariant from "~/utils/invariant";
import { logger } from "~/utils/logger";
import { assertUnreachable } from "~/utils/types";
@@ -259,7 +259,7 @@ export abstract class Bracket {
}
get participantTournamentTeamIds() {
- return removeDuplicates(
+ return R.unique(
this.data.match
.flatMap((match) => [match.opponent1?.id, match.opponent2?.id])
.filter(Boolean),
@@ -472,7 +472,7 @@ class SingleEliminationBracket extends Bracket {
}
private hasThirdPlaceMatch() {
- return removeDuplicates(this.data.match.map((m) => m.group_id)).length > 1;
+ return R.unique(this.data.match.map((m) => m.group_id)).length > 1;
}
get standings(): Standing[] {
@@ -511,7 +511,7 @@ class SingleEliminationBracket extends Bracket {
this.participantTournamentTeamIds.length - teams.length;
const result: Standing[] = [];
- for (const roundId of removeDuplicates(teams.map((team) => team.lostAt))) {
+ for (const roundId of R.unique(teams.map((team) => team.lostAt))) {
const teamsLostThisRound: { id: number }[] = [];
while (teams.length && teams[0].lostAt === roundId) {
teamsLostThisRound.push(teams.shift()!);
@@ -669,7 +669,7 @@ class DoubleEliminationBracket extends Bracket {
this.participantTournamentTeamIds.length - teams.length;
const result: Standing[] = [];
- for (const roundId of removeDuplicates(teams.map((team) => team.lostAt))) {
+ for (const roundId of R.unique(teams.map((team) => team.lostAt))) {
const teamsLostThisRound: { id: number }[] = [];
while (teams.length && teams[0].lostAt === roundId) {
teamsLostThisRound.push(teams.shift()!);
@@ -892,9 +892,7 @@ class RoundRobinBracket extends Bracket {
const relevantMatchesFinished =
standings.length === this.participantTournamentTeamIds.length;
- const uniquePlacements = removeDuplicates(
- standings.map((s) => s.placement),
- );
+ const uniquePlacements = R.unique(standings.map((s) => s.placement));
// 1,3,5 -> 1,2,3 e.g.
const placementNormalized = (p: number) => {
@@ -1182,9 +1180,7 @@ class SwissBracket extends Bracket {
});
});
- const uniquePlacements = removeDuplicates(
- standings.map((s) => s.placement),
- );
+ const uniquePlacements = R.unique(standings.map((s) => s.placement));
// 1,3,5 -> 1,2,3 e.g.
const placementNormalized = (p: number) => {
diff --git a/app/features/tournament-bracket/core/PickBan.ts b/app/features/tournament-bracket/core/PickBan.ts
index 85d8c16df..325d89375 100644
--- a/app/features/tournament-bracket/core/PickBan.ts
+++ b/app/features/tournament-bracket/core/PickBan.ts
@@ -1,3 +1,4 @@
+import * as R from "remeda";
import type { TournamentRoundMaps } from "~/db/tables";
import type {
ModeShort,
@@ -5,7 +6,6 @@ import type {
StageId,
} from "~/modules/in-game-lists";
import type { TournamentMapListMap } from "~/modules/tournament-map-list-generator";
-import { removeDuplicates } from "~/utils/arrays";
import invariant from "~/utils/invariant";
import { logger } from "~/utils/logger";
import { assertUnreachable } from "~/utils/types";
@@ -132,7 +132,7 @@ export function mapsListWithLegality(args: MapListWithStatusesArgs) {
}
})();
- const modesIncluded = removeDuplicates(mapPool.map((m) => m.mode));
+ const modesIncluded = R.unique(mapPool.map((m) => m.mode));
const unavailableStagesSet = unavailableStages(args);
const unavailableModesSetAll = unavailableModes(args);
diff --git a/app/features/tournament-bracket/core/PreparedMaps.ts b/app/features/tournament-bracket/core/PreparedMaps.ts
index a358b09a4..c4fff984a 100644
--- a/app/features/tournament-bracket/core/PreparedMaps.ts
+++ b/app/features/tournament-bracket/core/PreparedMaps.ts
@@ -1,6 +1,6 @@
-import compare from "just-compare";
+import * as R from "remeda";
import type { PreparedMaps } from "~/db/tables";
-import { nullFilledArray, removeDuplicates } from "~/utils/arrays";
+import { nullFilledArray } from "~/utils/arrays";
import invariant from "~/utils/invariant";
import type { Bracket } from "./Bracket";
import type { Tournament } from "./Tournament";
@@ -32,11 +32,11 @@ export function resolvePreparedForTheBracket({
] of tournament.ctx.settings.bracketProgression.entries()) {
if (
bracket.type === bracketPreparingFor.type &&
- compare(
+ R.isDeepEqual(
bracket.sources?.map((s) => s.bracketIdx),
bracketPreparingFor.sources?.map((s) => s.bracketIdx),
) &&
- compare(bracket.settings, bracketPreparingFor.settings)
+ R.isDeepEqual(bracket.settings, bracketPreparingFor.settings)
) {
const bracketMaps = preparedByBracket?.[anotherBracketIdx];
@@ -121,7 +121,7 @@ function trimMapsByTeamCount({
nullFilledArray(teamCount).map((_, i) => i + 1),
).round;
- const groupIds = removeDuplicates(preparedMaps.maps.map((r) => r.groupId));
+ const groupIds = R.unique(preparedMaps.maps.map((r) => r.groupId));
const result = { ...preparedMaps };
for (const groupId of groupIds) {
@@ -179,7 +179,7 @@ function thirdPlaceMatchDisappeared({
}
const preparedHasThirdPlace =
- removeDuplicates(preparedMaps.maps.map((r) => r.groupId)).length > 1;
+ R.unique(preparedMaps.maps.map((r) => r.groupId)).length > 1;
return preparedHasThirdPlace && teamCount < 4;
}
diff --git a/app/features/tournament-bracket/core/Progression.ts b/app/features/tournament-bracket/core/Progression.ts
index ee1b9af63..d7afe12ac 100644
--- a/app/features/tournament-bracket/core/Progression.ts
+++ b/app/features/tournament-bracket/core/Progression.ts
@@ -1,6 +1,4 @@
-// todo
-
-import compare from "just-compare";
+import * as R from "remeda";
import type { Tables, TournamentStageSettings } from "~/db/tables";
import { TOURNAMENT } from "~/features/tournament/tournament-constants";
import {
@@ -582,7 +580,7 @@ export function changedBracketProgression(
const oldBracket = oldProgression[i];
const newBracket = newProgression.at(i);
- if (!newBracket || !compare(oldBracket, newBracket)) {
+ if (!newBracket || !R.isDeepEqual(oldBracket, newBracket)) {
changed.push(i);
}
}
@@ -604,7 +602,7 @@ export function changedBracketProgressionFormat(
!newBracket ||
newBracket.name !== oldBracket.name ||
newBracket.type !== oldBracket.type ||
- !compare(newBracket.settings, oldBracket.settings)
+ !R.isDeepEqual(newBracket.settings, oldBracket.settings)
) {
return true;
}
diff --git a/app/features/tournament-bracket/core/Tournament.ts b/app/features/tournament-bracket/core/Tournament.ts
index 4ec17df89..545836afc 100644
--- a/app/features/tournament-bracket/core/Tournament.ts
+++ b/app/features/tournament-bracket/core/Tournament.ts
@@ -1,3 +1,4 @@
+import * as R from "remeda";
import type {
Tables,
TournamentStage,
@@ -13,7 +14,6 @@ import type { Match, Stage } from "~/modules/brackets-model";
import type { ModeShort } from "~/modules/in-game-lists";
import { modesShort, rankedModesShort } from "~/modules/in-game-lists/modes";
import { isAdmin } from "~/permissions";
-import { removeDuplicates } from "~/utils/arrays";
import {
databaseTimestampNow,
databaseTimestampToDate,
@@ -663,7 +663,7 @@ export class Tournament {
return ["CB"];
}
default: {
- const pickedModes = removeDuplicates(
+ const pickedModes = R.unique(
this.ctx.toSetMapPool.map((map) => map.mode),
);
if (pickedModes.length === 0) {
diff --git a/app/features/tournament-bracket/core/rounds.ts b/app/features/tournament-bracket/core/rounds.ts
index 7d5416769..f368c1d94 100644
--- a/app/features/tournament-bracket/core/rounds.ts
+++ b/app/features/tournament-bracket/core/rounds.ts
@@ -1,5 +1,5 @@
+import * as R from "remeda";
import type { TournamentManagerDataSet } from "~/modules/brackets-manager/types";
-import { removeDuplicates } from "~/utils/arrays";
import { TOURNAMENT } from "../../tournament/tournament-constants";
export function getRounds(args: {
@@ -52,7 +52,7 @@ export function getRounds(args: {
const hasThirdPlaceMatch =
args.type === "single" &&
- removeDuplicates(args.bracketData.match.map((m) => m.group_id)).length > 1;
+ R.unique(args.bracketData.match.map((m) => m.group_id)).length > 1;
const namedRounds = rounds.map((round, i) => {
const name = () => {
if (
diff --git a/app/features/tournament-bracket/core/summarizer.server.ts b/app/features/tournament-bracket/core/summarizer.server.ts
index 1675fdf56..421da6373 100644
--- a/app/features/tournament-bracket/core/summarizer.server.ts
+++ b/app/features/tournament-bracket/core/summarizer.server.ts
@@ -1,12 +1,11 @@
-import shuffle from "just-shuffle";
import type { Rating } from "node_modules/openskill/dist/types";
import { ordinal } from "openskill";
+import * as R from "remeda";
import {
identifierToUserIds,
rate,
userIdsToIdentifier,
} from "~/features/mmr/mmr-utils";
-import { removeDuplicates } from "~/utils/arrays";
import invariant from "~/utils/invariant";
import type { Tables } from "../../../db/tables";
import type { AllMatchResult } from "../queries/allMatchResultsByTournamentId.server";
@@ -134,12 +133,12 @@ export function calculateIndividualPlayerSkills({
: match.opponentTwo.id;
const participants = match.maps.flatMap((m) => m.participants);
- const winnerUserIds = removeDuplicates(
+ const winnerUserIds = R.unique(
participants
.filter((p) => p.tournamentTeamId === winnerTeamId)
.map((p) => p.userId),
);
- const loserUserIds = removeDuplicates(
+ const loserUserIds = R.unique(
participants
.filter((p) => p.tournamentTeamId !== winnerTeamId)
.map((p) => p.userId),
@@ -282,7 +281,7 @@ function selectMostPopular
(items: T[]): T {
return mostPopularItems[0][0];
}
- return shuffle(mostPopularItems)[0][0];
+ return R.shuffle(mostPopularItems)[0][0];
}
function mapResultDeltas(
diff --git a/app/features/tournament-bracket/core/tests/test-utils.ts b/app/features/tournament-bracket/core/tests/test-utils.ts
index 6693c8b34..8518b928e 100644
--- a/app/features/tournament-bracket/core/tests/test-utils.ts
+++ b/app/features/tournament-bracket/core/tests/test-utils.ts
@@ -1,5 +1,5 @@
+import * as R from "remeda";
import type { TournamentManagerDataSet } from "~/modules/brackets-manager/types";
-import { removeDuplicates } from "~/utils/arrays";
import type * as Progression from "../Progression";
import { Tournament } from "../Tournament";
import type { TournamentData } from "../Tournament.server";
@@ -49,11 +49,12 @@ export const testTournament = ({
data?: TournamentManagerDataSet;
ctx?: Partial;
}) => {
- const participant = removeDuplicates(
- data.match
- .flatMap((m) => [m.opponent1?.id, m.opponent2?.id])
- .filter(Boolean),
- ) as number[];
+ const participant = R.pipe(
+ data.match,
+ R.flatMap((m) => [m.opponent1?.id, m.opponent2?.id]),
+ R.filter(R.isTruthy),
+ R.unique,
+ );
return new Tournament({
data,
diff --git a/app/features/tournament-bracket/core/toMapList.ts b/app/features/tournament-bracket/core/toMapList.ts
index a176c78cd..653935568 100644
--- a/app/features/tournament-bracket/core/toMapList.ts
+++ b/app/features/tournament-bracket/core/toMapList.ts
@@ -1,11 +1,10 @@
/** Map list generation logic for "TO pick" as in the map list is defined beforehand by TO and teams don't pick */
-import shuffle from "just-shuffle";
+import * as R from "remeda";
import type { Tables, TournamentRoundMaps } from "~/db/tables";
import type { Round } from "~/modules/brackets-model";
import type { ModeShort, StageId } from "~/modules/in-game-lists";
import { SENDOUQ_DEFAULT_MAPS } from "~/modules/tournament-map-list-generator/constants";
-import { removeDuplicates } from "~/utils/arrays";
import { logger } from "~/utils/logger";
import { assertUnreachable } from "~/utils/types";
@@ -159,8 +158,8 @@ function modeOrder({
iteration: number;
flavor: GenerateTournamentRoundMaplistArgs["flavor"];
}) {
- const modes = removeDuplicates(pool.map((x) => x.mode));
- const shuffledModes = shuffle(modes);
+ const modes = R.unique(pool.map((x) => x.mode));
+ const shuffledModes = R.shuffle(modes);
shuffledModes.sort((a, b) => {
const aFreq = modeFrequency.get(a) ?? 0;
const bFreq = modeFrequency.get(b) ?? 0;
@@ -265,9 +264,9 @@ function resolveStage(
}
}
- const stage = shuffle(equallyGoodOptionsIgnoringCombo)[0];
+ const stage = R.shuffle(equallyGoodOptionsIgnoringCombo)[0];
if (typeof stage !== "number") {
- const fallback = shuffle(SENDOUQ_DEFAULT_MAPS[mode].slice())[0];
+ const fallback = R.shuffle(SENDOUQ_DEFAULT_MAPS[mode])[0];
logger.warn(
`No stage found for mode ${mode} iteration ${currentIteration}, using fallback ${fallback}`,
);
diff --git a/app/features/tournament-bracket/tournament-bracket-utils.ts b/app/features/tournament-bracket/tournament-bracket-utils.ts
index b0b507226..350e5c43f 100644
--- a/app/features/tournament-bracket/tournament-bracket-utils.ts
+++ b/app/features/tournament-bracket/tournament-bracket-utils.ts
@@ -1,4 +1,5 @@
import type { TFunction } from "i18next";
+import * as R from "remeda";
import type { Tables, TournamentRoundMaps } from "~/db/tables";
import type { TournamentManagerDataSet } from "~/modules/brackets-manager/types";
import type { ModeShort, StageId } from "~/modules/in-game-lists";
@@ -7,8 +8,6 @@ import {
seededRandom,
sourceTypes,
} from "~/modules/tournament-map-list-generator";
-import { removeDuplicates } from "~/utils/arrays";
-import { sumArray } from "~/utils/number";
import type { FindMatchById } from "../tournament-bracket/queries/findMatchById.server";
import type { TournamentLoaderData } from "../tournament/loaders/to.$id.server";
import type { Standing } from "./core/Bracket";
@@ -110,7 +109,7 @@ export function everyMatchIsOver(
) {
// winners, losers & grand finals+bracket reset are all different stages
const isDoubleElimination =
- removeDuplicates(bracket.match.map((match) => match.group_id)).length === 3;
+ R.unique(bracket.match.map((match) => match.group_id)).length === 3;
// tournament didn't start yet
if (bracket.match.length === 0) return false;
@@ -241,7 +240,7 @@ export function isSetOverByResults({
}
if (countType === "PLAY_ALL") {
- return sumArray(Array.from(winCounts.values())) === count;
+ return R.sum(Array.from(winCounts.values())) === count;
}
const maxWins = Math.max(...Array.from(winCounts.values()));
@@ -260,7 +259,7 @@ export function isSetOverByScore({
countType: TournamentRoundMaps["type"];
}) {
if (countType === "PLAY_ALL") {
- return sumArray(scores) === count;
+ return R.sum(scores) === count;
}
const matchOverAtXWins = Math.ceil(count / 2);
diff --git a/app/features/tournament/core/Standings.ts b/app/features/tournament/core/Standings.ts
index 99230636f..1c0aed947 100644
--- a/app/features/tournament/core/Standings.ts
+++ b/app/features/tournament/core/Standings.ts
@@ -1,7 +1,7 @@
+import * as R from "remeda";
import type { Standing } from "~/features/tournament-bracket/core/Bracket";
import * as Progression from "~/features/tournament-bracket/core/Progression";
import type { Tournament } from "~/features/tournament-bracket/core/Tournament";
-import { removeDuplicates } from "~/utils/arrays";
/** Calculates SPR (Seed Performance Rating) - see https://www.pgstats.com/articles/introducing-spr-and-uf */
export function calculateSPR({
@@ -11,7 +11,7 @@ export function calculateSPR({
standings: Standing[];
teamId: number;
}) {
- const uniquePlacements = removeDuplicates(
+ const uniquePlacements = R.unique(
standings.map((standing) => standing.placement),
).sort((a, b) => a - b);
diff --git a/app/features/tournament/queries/setHistoryByTeamId.server.ts b/app/features/tournament/queries/setHistoryByTeamId.server.ts
index 1fbbff89a..65792375f 100644
--- a/app/features/tournament/queries/setHistoryByTeamId.server.ts
+++ b/app/features/tournament/queries/setHistoryByTeamId.server.ts
@@ -1,7 +1,7 @@
+import * as R from "remeda";
import { sql } from "~/db/sql";
import type { Tables } from "~/db/tables";
import type { ModeShort, StageId } from "~/modules/in-game-lists";
-import { removeDuplicatesByProperty } from "~/utils/arrays";
import { parseDBArray } from "~/utils/sql";
const stm = sql.prepare(/* sql */ `
@@ -111,10 +111,7 @@ export function setHistoryByTeamId(
...row,
matches: parseDBArray(row.matches),
// TODO: there is probably a way to do this in SQL
- players: removeDuplicatesByProperty(
- parseDBArray(row.players),
- (u: Pick) => u.id,
- ),
+ players: R.uniqueBy(parseDBArray(row.players), (u) => u.id),
};
});
}
diff --git a/app/features/tournament/routes/to.$id.register.tsx b/app/features/tournament/routes/to.$id.register.tsx
index 7d17d4881..1526cea8e 100644
--- a/app/features/tournament/routes/to.$id.register.tsx
+++ b/app/features/tournament/routes/to.$id.register.tsx
@@ -36,7 +36,6 @@ import { useAutoRerender } from "~/hooks/useAutoRerender";
import { useIsMounted } from "~/hooks/useIsMounted";
import { useSearchParamState } from "~/hooks/useSearchParamState";
import { modesShort, rankedModesShort } from "~/modules/in-game-lists/modes";
-import { filterOutFalsy } from "~/utils/arrays";
import invariant from "~/utils/invariant";
import {
LOG_IN_URL,
@@ -403,10 +402,7 @@ function RegistrationProgress({
const completedIfTruthy = (condition: unknown) =>
condition ? "completed" : "incomplete";
- const steps: Array<{
- name: string;
- status: "completed" | "incomplete" | "notice";
- }> = filterOutFalsy([
+ const steps = [
{
name: t("tournament:pre.steps.name"),
status: completedIfTruthy(name),
@@ -432,10 +428,10 @@ function RegistrationProgress({
tournament.isLeagueSignup
? {
name: "Google Sheet",
- status: "notice",
+ status: "notice" as const,
}
: null,
- ]);
+ ].filter((step) => step !== null);
const regClosesBeforeStart =
tournament.registrationClosesAt.getTime() !==
diff --git a/app/features/user-page/actions/u.$identifier.builds.new.server.ts b/app/features/user-page/actions/u.$identifier.builds.new.server.ts
index 2a34358a1..73562e17e 100644
--- a/app/features/user-page/actions/u.$identifier.builds.new.server.ts
+++ b/app/features/user-page/actions/u.$identifier.builds.new.server.ts
@@ -1,4 +1,5 @@
import { type ActionFunction, redirect } from "@remix-run/node";
+import * as R from "remeda";
import { z } from "zod";
import { BUILD } from "~/constants";
import { requireUser } from "~/features/auth/core/user.server";
@@ -15,7 +16,6 @@ import type {
BuildAbilitiesTuple,
MainWeaponId,
} from "~/modules/in-game-lists/types";
-import { removeDuplicates } from "~/utils/arrays";
import { unJsonify } from "~/utils/kysely.server";
import { logger } from "~/utils/logger";
import { errorToastIfFalsy, parseRequestPayload } from "~/utils/remix.server";
@@ -194,7 +194,7 @@ function refreshCache({
...oldBuildWeapons.map(({ weaponSplId }) => weaponSplId),
];
- const dedupedWeaponSplIds = removeDuplicates(allWeaponSplIds);
+ const dedupedWeaponSplIds = R.unique(allWeaponSplIds);
refreshBuildsCacheByWeaponSplIds(dedupedWeaponSplIds);
}
diff --git a/app/features/vods/queries/findVods.server.ts b/app/features/vods/queries/findVods.server.ts
index 4a009db6b..811c3ed3a 100644
--- a/app/features/vods/queries/findVods.server.ts
+++ b/app/features/vods/queries/findVods.server.ts
@@ -1,7 +1,7 @@
+import * as R from "remeda";
import { sql } from "~/db/sql";
import type { Tables } from "~/db/tables";
import type { MainWeaponId, ModeShort, StageId } from "~/modules/in-game-lists";
-import { removeDuplicates } from "~/utils/arrays";
import { parseDBArray, parseDBJsonArray } from "~/utils/sql";
import { weaponIdToArrayWithAlts } from "../../../modules/in-game-lists/weapon-ids";
import { VODS_PAGE_BATCH_SIZE } from "../vods-constants";
@@ -87,7 +87,7 @@ export function findVods({
return {
...vod,
- weapons: removeDuplicates(parseDBArray(vod.weapons)),
+ weapons: R.unique(parseDBArray(vod.weapons)),
pov: playerNames[0] ?? players[0],
};
})
diff --git a/app/utils/arrays.ts b/app/utils/arrays.ts
index 554a4a920..d287ce0cd 100644
--- a/app/utils/arrays.ts
+++ b/app/utils/arrays.ts
@@ -1,9 +1,6 @@
// TODO: when more examples of permissions profile difference between
// this implementation and one that takes arrays
-import shuffle from "just-shuffle";
-import invariant from "~/utils/invariant";
-
// (not all arrays need to necessarily run but they need to be defined)
export function allTruthy(arr: unknown[]) {
return arr.every(Boolean);
@@ -55,48 +52,10 @@ export function isDefined(value: T | undefined | null): value is T {
return value !== null && value !== undefined;
}
-export function removeDuplicates(arr: T[]): T[] {
- const seen = new Set();
-
- return arr.filter((item) => {
- if (seen.has(item)) return false;
- seen.add(item);
-
- return true;
- });
-}
-
-export function removeDuplicatesByProperty(
- 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): null[] {
return new Array(size).fill(null);
}
-export function pickRandomItem(array: T[]): T {
- invariant(array.length > 0, "Can't pick from empty array");
-
- const shuffled = shuffle(structuredClone(array));
-
- return shuffled[0];
-}
-
-export function filterOutFalsy(arr: (T | null | undefined)[]): T[] {
- return arr.filter(Boolean) as T[];
-}
-
/**
* Calculates the average of an array of numbers. If the array is empty, returns null.
*
diff --git a/app/utils/number.ts b/app/utils/number.ts
index 735b8ffc2..7999fa019 100644
--- a/app/utils/number.ts
+++ b/app/utils/number.ts
@@ -1,3 +1,5 @@
+import * as R from "remeda";
+
export function roundToNDecimalPlaces(num: number, n = 2) {
return Number((Math.round(num * 10 ** n) / 10 ** n).toFixed(n));
}
@@ -9,12 +11,8 @@ export function cutToNDecimalPlaces(num: number, n = 2) {
return Number(n > 0 ? result.replace(/\.?0+$/, "") : result);
}
-export function sumArray(arr: number[]) {
- return arr.reduce((acc, curr) => acc + curr, 0);
-}
-
export function averageArray(arr: number[]) {
- return sumArray(arr) / arr.length;
+ return R.sum(arr) / arr.length;
}
export function safeNumberParse(value: string | null) {
diff --git a/app/utils/strings.ts b/app/utils/strings.ts
index 99737663d..4784bbe54 100644
--- a/app/utils/strings.ts
+++ b/app/utils/strings.ts
@@ -5,10 +5,6 @@ export function inGameNameWithoutDiscriminator(inGameName: string) {
return inGameName.split("#")[0];
}
-export function semiRandomId() {
- return String(Math.random());
-}
-
export const rawSensToString = (sens: number) =>
`${sens > 0 ? "+" : ""}${sens / 10}`;
@@ -53,10 +49,6 @@ export function gearTypeToInitial(gearType: GearType) {
}
}
-export function capitalize(str: string) {
- return str[0].toUpperCase() + str.slice(1);
-}
-
export function pathnameFromPotentialURL(maybeUrl: string) {
try {
return new URL(maybeUrl).pathname.replace("/", "");
diff --git a/package-lock.json b/package-lock.json
index 72f72fb0a..f84d6f99b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -33,10 +33,6 @@
"i18next-browser-languagedetector": "^8.0.4",
"i18next-http-backend": "^2.6.2",
"isbot": "^5.1.25",
- "just-capitalize": "^3.2.0",
- "just-compare": "^2.3.0",
- "just-random-integer": "^4.2.0",
- "just-shuffle": "^4.2.0",
"kysely": "^0.27.6",
"lru-cache": "^11.1.0",
"markdown-to-jsx": "^7.7.4",
@@ -56,6 +52,7 @@
"react-i18next": "^15.4.1",
"react-use": "^17.6.0",
"react-use-draggable-scroll": "^0.4.7",
+ "remeda": "^2.21.2",
"remix-auth": "^4.1.0",
"remix-auth-oauth2": "^3.4.0",
"remix-i18next": "^6.4.1",
@@ -11131,30 +11128,6 @@
"graceful-fs": "^4.1.6"
}
},
- "node_modules/just-capitalize": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/just-capitalize/-/just-capitalize-3.2.0.tgz",
- "integrity": "sha512-FK8U9A5AHCIGxlEXg3RFJkb9Nz/fS9luJlrfRf0bFBZU6xnIQ6tbwl+HitMJLwCFszZqVaXQcyeoy8/PYABS6A==",
- "license": "MIT"
- },
- "node_modules/just-compare": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/just-compare/-/just-compare-2.3.0.tgz",
- "integrity": "sha512-6shoR7HDT+fzfL3gBahx1jZG3hWLrhPAf+l7nCwahDdT9XDtosB9kIF0ZrzUp5QY8dJWfQVr5rnsPqsbvflDzg==",
- "license": "MIT"
- },
- "node_modules/just-random-integer": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/just-random-integer/-/just-random-integer-4.2.0.tgz",
- "integrity": "sha512-MfabwcY+RQNCVCmZZkTYDpk/AT315+7Rkoj59+abzYxgUOiSdoZ4G5hbK/4VZqdHScu95Wnd+1MqxyUETM6xuQ==",
- "license": "MIT"
- },
- "node_modules/just-shuffle": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/just-shuffle/-/just-shuffle-4.2.0.tgz",
- "integrity": "sha512-/dDmNseAWLf3XkFY9xf3/BdQoiy27LNUy/7uG4zdSAX526nIHMYPYeJ4pN4lT1/pgNEX8XCXPtUB6gJqTpBEng==",
- "license": "MIT"
- },
"node_modules/jwa": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
@@ -14695,6 +14668,15 @@
"integrity": "sha512-agFFS3RzrLXJl5LY5xg/xYyXvUuVAnkhgKO7RaO9J1Ssth6yvbO+PIiV67V59MB5NCdAK2flvGvNT4mdKVniFA==",
"license": "MIT"
},
+ "node_modules/remeda": {
+ "version": "2.21.2",
+ "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.21.2.tgz",
+ "integrity": "sha512-wdhkMDou8HRpD7RnxKJ/FHJWEGXRH7jV/pb0NsdLLSoBo+G9RjtxcY41hVhogLfEMkThk6aySKjs+Yd6PnpzBA==",
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^4.37.0"
+ }
+ },
"node_modules/remix-auth": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/remix-auth/-/remix-auth-4.1.0.tgz",
@@ -16212,9 +16194,9 @@
"license": "ISC"
},
"node_modules/type-fest": {
- "version": "4.36.0",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.36.0.tgz",
- "integrity": "sha512-3T/PUdKTCnkUmhQU6FFJEHsLwadsRegktX3TNHk+2JJB9HlA8gp1/VXblXVDI93kSnXF2rdPx0GMbHtJIV2LPg==",
+ "version": "4.39.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.39.1.tgz",
+ "integrity": "sha512-uW9qzd66uyHYxwyVBYiwS4Oi0qZyUqwjU+Oevr6ZogYiXt99EOYtwvzMSLw1c3lYo2HzJsep/NB23iEVEgjG/w==",
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=16"
diff --git a/package.json b/package.json
index 17a46fec2..7f40a7aa9 100644
--- a/package.json
+++ b/package.json
@@ -49,10 +49,6 @@
"i18next-browser-languagedetector": "^8.0.4",
"i18next-http-backend": "^2.6.2",
"isbot": "^5.1.25",
- "just-capitalize": "^3.2.0",
- "just-compare": "^2.3.0",
- "just-random-integer": "^4.2.0",
- "just-shuffle": "^4.2.0",
"kysely": "^0.27.6",
"lru-cache": "^11.1.0",
"markdown-to-jsx": "^7.7.4",
@@ -72,6 +68,7 @@
"react-i18next": "^15.4.1",
"react-use": "^17.6.0",
"react-use-draggable-scroll": "^0.4.7",
+ "remeda": "^2.21.2",
"remix-auth": "^4.1.0",
"remix-auth-oauth2": "^3.4.0",
"remix-i18next": "^6.4.1",