isaacscript-common
Version:
Helper functions and features for IsaacScript mods.
252 lines (227 loc) • 7.48 kB
text/typescript
import { ReadonlySet } from "../types/ReadonlySet";
import { getArrayCombinations, getRandomArrayElement, sumArray } from "./array";
import { sortNormal } from "./sort";
import { isPrimitive } from "./types";
/**
* Helper function to add all of the values in one set to another set. The first set passed will be
* modified in place.
*
* This function is variadic, meaning that you can specify N sets to add to the first set.
*/
export function addSetsToSet<T>(
// eslint-disable-next-line complete/prefer-readonly-parameter-types
mainSet: Set<T>,
...setsToAdd: ReadonlyArray<ReadonlySet<T>>
): void {
for (const set of setsToAdd) {
for (const value of set) {
mainSet.add(value);
}
}
}
/**
* Helper function to create a new set that is the composition of two or more sets.
*
* This function is variadic, meaning that you can specify N sets.
*/
export function combineSets<T>(
...sets: ReadonlyArray<ReadonlySet<T>>
): ReadonlySet<T> {
const newSet = new Set<T>();
for (const set of sets) {
for (const value of set) {
newSet.add(value);
}
}
return newSet;
}
/** Helper function to copy a set. (You can also use a Set constructor to accomplish this task.) */
// eslint-disable-next-line complete/no-mutable-return
export function copySet<T>(oldSet: ReadonlySet<T>): Set<T> {
const newSet = new Set<T>();
for (const value of oldSet) {
newSet.add(value);
}
return newSet;
}
/**
* Helper function to delete all of the values in one set from another set. The first set passed
* will be modified in place.
*
* This function is variadic, meaning that you can specify N sets to remove from the first set.
*/
export function deleteSetsFromSet<T>(
// eslint-disable-next-line complete/prefer-readonly-parameter-types
mainSet: Set<T>,
...setsToRemove: ReadonlyArray<ReadonlySet<T>>
): void {
for (const set of setsToRemove) {
for (const value of set) {
mainSet.delete(value);
}
}
}
/**
* Helper function to get a random element from the provided set.
*
* If you want to get an unseeded element, you must explicitly pass `undefined` to the `seedOrRNG`
* parameter.
*
* @param set The set to get an element from.
* @param seedOrRNG The `Seed` or `RNG` object to use. If an `RNG` object is provided, the
* `RNG.Next` method will be called. If `undefined` is provided, it will default to
* a random seed.
* @param exceptions Optional. An array of elements to skip over if selected.
*/
export function getRandomSetElement<T extends number | string>(
set: ReadonlySet<T>,
seedOrRNG: Seed | RNG | undefined,
exceptions: readonly T[] = [],
): T {
const array = getSortedSetValues(set);
return getRandomArrayElement(array, seedOrRNG, exceptions);
}
/**
* Helper function to get all possible combinations of the given set. This includes the combination
* of an empty set.
*
* For example, if this function is provided a set containing 1, 2, and 3, then it will return an
* array containing the following sets:
*
* - [] (if `includeEmptyArray` is set to true)
* - [1]
* - [2]
* - [3]
* - [1, 2]
* - [1, 3]
* - [2, 3]
* - [1, 2, 3]
*
* @param set The set to get the combinations of.
* @param includeEmptyArray Whether to include an empty array in the combinations.
*/
export function getSetCombinations<T extends number | string>(
set: ReadonlySet<T>,
includeEmptyArray: boolean,
): ReadonlyArray<ReadonlySet<T>> {
const values = getSortedSetValues(set);
const combinations = getArrayCombinations(values, includeEmptyArray);
return combinations.map((array) => new ReadonlySet(array));
}
/**
* Helper function to get a sorted array based on the contents of a set.
*
* Normally, set values are returned in insertion order, so use this function when the ordering of
* the contents is important.
*/
export function getSortedSetValues<T extends number | string>(
set: ReadonlySet<T>,
// eslint-disable-next-line complete/no-mutable-return
): T[] {
const values = [...set];
// Check for problematic types in order to throw a helpful error message.
const firstElement = values[0];
if (firstElement !== undefined) {
const arrayType = type(firstElement);
if (!isPrimitive(arrayType)) {
error(
`Failed to get the sorted set values because the provided set was of type "${arrayType}". Having sets with non-primitive types doesn't make much sense in general, so you might need to rethink what you are doing.`,
);
}
}
values.sort(sortNormal);
return values;
}
/**
* Helper function to convert the keys of an object to a read-only set.
*
* Note that the set values will be inserted in a random order, due to how `pairs` works under the
* hood.
*
* Also see the `objectKeysToSet` function.
*/
export function objectKeysToReadonlySet<K extends string | number | symbol, V>(
object: Record<K, V>,
): ReadonlySet<K> {
return objectKeysToSet(object);
}
/**
* Helper function to convert the keys of an object to a set.
*
* Note that the set values will be inserted in a random order, due to how `pairs` works under the
* hood.
*
* Also see the `objectKeysToReadonlySet` function.
*/
export function objectKeysToSet<K extends string | number | symbol, V>(
object: Record<K, V>,
// eslint-disable-next-line complete/no-mutable-return
): Set<K> {
const set = new Set<K>();
for (const key of Object.keys(object)) {
set.add(key as K);
}
return set;
}
/**
* Helper function to convert the values of an object to a read-only set.
*
* Note that the set values will be inserted in a random order, due to how `pairs` works under the
* hood.
*
* Also see the `objectValuesToSet` function.
*/
export function objectValuesToReadonlySet<
K extends string | number | symbol,
V,
>(object: Record<K, V>): ReadonlySet<V> {
return objectValuesToSet(object);
}
/**
* Helper function to convert the values of an object to a set.
*
* Note that the set values will be inserted in a random order, due to how `pairs` works under the
* hood.
*
* Also see the `objectValuesToReadonlySet` function.
*/
export function objectValuesToSet<K extends string | number | symbol, V>(
object: Record<K, V>,
// eslint-disable-next-line complete/no-mutable-return
): Set<V> {
const set = new Set<V>();
for (const key of Object.values(object)) {
set.add(key as V);
}
return set;
}
/**
* Helper function to add one or more elements to a set at once without having to repeatedly call
* the `Set.add` method.
*
* This function is variadic, meaning that you can pass as many things as you want to add.
*/
// eslint-disable-next-line complete/prefer-readonly-parameter-types
export function setAdd<T>(set: Set<T>, ...elements: readonly T[]): void {
for (const element of elements) {
set.add(element);
}
}
/**
* Helper function to check for one or more elements in a set at once without having to repeatedly
* call the `Set.has` method.
*
* This function is variadic, meaning that you can pass as many things as you want to check for. It
* will return true if one or more elements are found.
*/
export function setHas<T>(
set: ReadonlySet<T>,
...elements: readonly T[]
): boolean {
return elements.some((element) => set.has(element));
}
/** Helper function to sum every value in a set together. */
export function sumSet(set: ReadonlySet<number>): number {
const values = [...set];
return sumArray(values);
}