Skip to content

Instantly share code, notes, and snippets.

@ralphschuler
Last active September 25, 2023 13:49
Show Gist options
  • Save ralphschuler/c0f4a9d7d5a4cdb80d98a9faf65ecc3a to your computer and use it in GitHub Desktop.
Save ralphschuler/c0f4a9d7d5a4cdb80d98a9faf65ecc3a to your computer and use it in GitHub Desktop.
Typescript Code Snippets I (found/wrote/generated using ai)
class BetterMap<k, v> extends Map<k, v> {
update(key:k, updater: (v:v, k:k) => v, notset:v = undefined) {
if(this.has(key)) this.set(key, updater(this.get(key), key))
else this.set(key, notset)
}
filter(predicate: (v:v, k:k) => boolean) {
const newMap = new BetterMap<k, v>()
const entries = Array.from(this.entries())
for(const [key, value] of entries) {
if(predicate(value, key)) newMap.set(key, value)
}
return newMap
}
merge(map: Map<k, v>, resolve: (k:k, a:v, b:v) => v = (k, a, b) => b) {
const entries = Array.from(map.entries())
for(const [key, value] of entries) {
if(this.has(key)) this.set(key, resolve(key, this.get(key), value))
else this.set(key, value)
}
}
}
class BetterSet<v> extends Set<v> {
filter(predicate: (v: v) => boolean) {
const newSet = new BetterSet<v>()
const entries = Array.from(this.entries())
for(const [value] of entries) {
if (predicate(value)) newSet.add(value)
}
return newSet
}
merge(set: Set<v>) {
const entries = Array.from(set.entries())
for (const kv of entries) {
this.add(kv[0])
}
}
except(set: Set<v>) {
const newSet = new Set<v>()
const entries = Array.from(set.entries())
for(const [value] of entries) {
if(!this.has(value)) newSet.add(value)
}
return newSet
}
both(set: Set<v>) {
const newSet = new Set<v>()
const entries = Array.from(set.entries())
for(const [value] of entries) {
if (this.has(value)) newSet.add(value)
}
return newSet
}
}
interface Connection<T> {
weight: number;
neuron: Neuron<T>;
}
class Neuron<T> {
private incomingConnections: Connection<T>[] = [];
private outgoingConnections: Connection<T>[] = [];
private bias: number;
constructor(bias: number) {
this.bias = bias;
}
connect(neuron: Neuron<T>, weight: number): void {
this.outgoingConnections.push({ weight, neuron });
neuron.incomingConnections.push({ weight, neuron: this });
}
activate(input: T): T {
let sum = 0;
for (const connection of this.incomingConnections) {
sum += connection.weight * connection.neuron.activate(input);
}
return (input as any) + sum; // We assume T is a numeric type here, you can adjust as needed.
}
propagate(error: T): void {
for (const connection of this.outgoingConnections) {
connection.neuron.propagate(error as any); // Adjust as needed based on T.
}
}
toObject(): object {
return {
bias: this.bias,
incomingConnections: this.incomingConnections.map(connection => ({
weight: connection.weight,
neuron: connection.neuron.toObject()
})),
outgoingConnections: this.outgoingConnections.map(connection => ({
weight: connection.weight,
neuron: connection.neuron.toObject()
}))
};
}
}
class Layer<T> {
private neurons: Neuron<T>[] = [];
constructor(length: number, createNeuron: (bias: number) => Neuron<T>) {
for (let i = 0; i < length; i++) {
this.neurons.push(createNeuron(Math.random()));
}
}
connect(layer: Layer<T>): void {
for (const neuronA of this.neurons) {
for (const neuronB of layer.neurons) {
neuronA.connect(neuronB, Math.random());
}
}
}
toObject(): object {
return {
neurons: this.neurons.map(neuron => neuron.toObject())
};
}
}
class Network<T> {
private inputLayer: Layer<T>;
private hiddenLayers: Layer<T>[] = [];
private outputLayer: Layer<T>;
constructor(
inputLayerLength: number,
hiddenLayerLengths: number[],
outputLayerLength: number,
createNeuron: (bias: number) => Neuron<T>
) {
this.inputLayer = new Layer(inputLayerLength, createNeuron);
for (const length of hiddenLayerLengths) {
this.hiddenLayers.push(new Layer(length, createNeuron));
}
this.outputLayer = new Layer(outputLayerLength, createNeuron);
}
connect(): void {
this.inputLayer.connect(this.hiddenLayers[0]);
for (let i = 0; i < this.hiddenLayers.length - 1; i++) {
this.hiddenLayers[i].connect(this.hiddenLayers[i + 1]);
}
this.hiddenLayers[this.hiddenLayers.length - 1].connect(this.outputLayer);
}
activate(input: T[]): T[] {
const output = this.inputLayer.neurons.map(
(neuron: Neuron<T>, index: number) => neuron.activate(input[index])
);
return this.outputLayer.neurons.map(
(neuron: Neuron<T>, index: number) => neuron.activate(output[index])
);
}
propagate(error: T[]): void {
const output = this.inputLayer.neurons.map(
(neuron: Neuron<T>, index: number) => neuron.activate(error[index])
);
this.outputLayer.neurons.map(
(neuron: Neuron<T>, index: number) => neuron.propagate(error[index])
);
}
train(input: T[], output: T[]): void {
const outputActivation = this.activate(input);
const error = output.map((value, index) => value - outputActivation[index]);
this.propagate(error);
}
toObject(): object {
return {
inputLayer: this.inputLayer.toObject(),
hiddenLayers: this.hiddenLayers.map(layer => layer.toObject()),
outputLayer: this.outputLayer.toObject(),
};
}
}
export {
Neuron,
Layer,
Network,
};
export type Rule = {
symbol: SymbolType;
transform: Array<SymbolType>;
probability: number;
}
export enum SymbolType {
North = "N",
East = "E",
South = "S",
West = "W",
Forward = "F",
Checkpoint = "C",
Jump = "J"
}
export class LSystem {
private axiom: string;
private rules: Array<Rule>;
constructor(axiom: string, rules: Rule[]) {
this.axiom = axiom;
this.rules = rules;
}
public generate(iterations: number): string {
let result = this.axiom;
for (let i = 0; i < iterations; i++) {
result = this.applyRules(result);
}
return result;
}
private applyRules(str: string): string {
let result = "";
for (let i = 0; i < str.length; i++) {
const rules = this.rules.filter(r => r.symbol === str[i]);
if (rules.length > 0) {
const rule = this.getRandomRule(rules);
result += rule.transform;
} else {
result += str[i];
}
}
return result;
}
private getRandomRule(rules: Rule[]): Rule {
const random = Math.random();
let sum = 0;
for (let i = 0; i < rules.length; i++) {
sum += rules[i].probability;
if (random < sum) {
return rules[i];
}
}
return rules[rules.length - 1];
}
}
/**
* Types for property and method mappings.
*/
type PropertyTypeMap = Record<string, Record<string, string>>;
type MethodTypeMap = Record<string, Record<string, Function>>;
const configProperties: PropertyTypeMap = {};
const interfaceMethods: MethodTypeMap = {};
/**
* Decorator to collect properties with their types.
* @param {string} type - The expected type of the property.
*/
export function ConfigProperty(type: string, defaultValue?: any) {
return (target: object, propertyKey: string) => {
addToMap(configProperties, target.constructor.name, propertyKey, type);
if (defaultValue !== undefined) {
target[propertyKey] = defaultValue;
}
};
}
/**
* Decorator to collect methods.
* @param {object} target - The target object.
* @param {string} propertyKey - The key of the property.
* @param {PropertyDescriptor} descriptor - The property descriptor.
*/
export function InterfaceMethod(target: object, propertyKey: string, descriptor: PropertyDescriptor) {
addToMap(interfaceMethods, target.constructor.name, propertyKey, descriptor.value);
}
/**
* Utility function to add an entry to a map.
* @param {Record<string, any>} map - The map to which to add the entry.
* @param {string} className - The name of the class.
* @param {string} key - The key to add.
* @param {any} value - The value to associate with the key.
*/
function addToMap(map: Record<string, any>, className: string, key: string, value: any) {
map[className] = map[className] || {};
map[className][key] = value;
}
/**
* Function to validate and freeze the unified config object.
* @param {UnifiedConfig<any>} config - The config object to validate.
* @param {Function[]} mixins - The array of mixin classes.
*/
function validateAndFreezeConfig(config: UnifiedConfig<any>, mixins: Function[]) {
Object.freeze(config);
mixins.forEach((Mixin) => {
const className = Mixin.name;
const expectedConfig = configProperties[className] || {};
const actualConfigKeys = Object.keys(expectedConfig);
actualConfigKeys.forEach((key) => {
if (config[key] === undefined) {
throw new Error(`Missing config property: ${key} in ${className}`);
}
const expectedType = expectedConfig[key];
const actualType = typeof config[key];
if (expectedType !== actualType) {
throw new Error(`Type mismatch for ${key} in ${className}: expected ${expectedType}, got ${actualType}`);
}
});
});
}
type ExtractConfig<T> = T extends new (config: infer Config) => any ? Config : never;
type UnifiedConfig<T extends any[]> = {
[K in keyof T]: ExtractConfig<T[K]>;
}[number];
/**
* Function to create a new class with mixin functionality.
* @param {...Function} mixins - The mixin classes to include.
* @returns {Promise<new (config: UnifiedConfig<T>) => any>} - The newly created class.
*/
export async function createClass<T extends Array<new (config: any) => any>>(
...mixins: T
): Promise<new (config: UnifiedConfig<T>) => any> {
// Perform any asynchronous operations here, if needed
return class {
constructor(config: UnifiedConfig<T>) {
validateAndFreezeConfig(config, mixins);
for (const Mixin of mixins) {
const mixinInstance = new Mixin(config);
Object.assign(this, mixinInstance);
}
}
};
}
// Example usage
interface ILoggingMixin {
logLevel: 'info' | 'warn' | 'error';
log(message: string): void;
}
class LoggingMixinInstance implements ILoggingMixin {
@ConfigProperty('string', 'info')
logLevel: 'info' | 'warn' | 'error';
@InterfaceMethod
log(message: string): void {
console.log(`[${this.logLevel}] ${message}`);
}
}
interface ICachingMixin {
cacheSize: number;
setCache(key: string, value: any): void;
}
class CachingMixinInstance implements ICachingMixin {
@ConfigProperty('number', 100)
cacheSize: number;
@InterfaceMethod
setCache(key: string, value: any): void {
// Implement caching logic here
}
}
// Usage
(async () => {
const MyClass = await createClass(LoggingMixinInstance, CachingMixinInstance);
const myInstance = new MyClass({
logLevel: 'info',
cacheSize: 100
});
myInstance.log('Hello, world!');
})();
export type ComparatorFunction<T> = (a: T, b: T) => number;
export type PriorityQueueOptions<T> = {
comparatorFunction: ComparatorFunction<T>;
initialValues?: Array<T>;
}
export type Optional<T> = T | null;
export class PriorityQueue<T> {
private values: Array<T|null> = []
private comparatorFunction: ComparatorFunction<T>;
private length: number = 0;
public constructor(options: PriorityQueueOptions<T>) {
this.comparatorFunction = options.comparatorFunction;
if (options.initialValues) {
options.initialValues.forEach(value => this.enqueue(value))
}
}
public enqueue(value: T): void {
if (this.values.length <= this.length) {
this.values.length = Math.max(1, this.values.length * 2)
}
this.values[this.length++] = value
this.bubbleUp();
}
public dequeue(): Optional<T|null> {
if (this.length === 0) {
return null
}
const node = this.values[0]
if (this.length === 1) {
this.length = 0
this.values[0] = null
return node
}
this.values[0] = this.values[this.length - 1]
this.values[this.length - 1] = null
this.length--
this.bubbleDown()
return node
}
public heapSort(): Array<T|null> {
return Array.from({length: this.length}, () => this.dequeue());
}
private parent(nodeIndex: number): number|null {
if (nodeIndex === 0) {
return null
}
return (nodeIndex - 1) >>> 1
}
private leftChild(nodeIndex: number): number|null {
const child = (nodeIndex * 2) + 1
if (child >= this.length ){
return null
}
return child
}
private rightChild(nodeIndex: number): number|null {
const child = (nodeIndex * 2) + 2
if (child >= this.length) {
return null
}
return child
}
private bubbleUp(nodeIndex: number = this.length - 1) {
const parent = this.parent(nodeIndex)
if (parent !== null && this.comparatorFunction(this.values[nodeIndex] as T, this.values[parent] as T) < 0) {
const node = this.values[nodeIndex]
this.values[nodeIndex] = this.values[parent]
this.values[parent] = node
this.bubbleUp(parent)
}
return
}
public bubbleDown(nodeIndex: number = 0) {
const leftChild = this.leftChild(nodeIndex)
const rightChild = this.rightChild(nodeIndex)
let swapCandidate = nodeIndex
if (leftChild !== null && this.comparatorFunction(this.values[swapCandidate] as T, this.values[leftChild] as T) > 0) {
swapCandidate = leftChild
}
if (rightChild !== null && this.comparatorFunction(this.values[swapCandidate] as T, this.values[rightChild] as T) > 0) {
swapCandidate = rightChild
}
if (swapCandidate !== nodeIndex) {
const node = this.values[nodeIndex]
this.values[nodeIndex] = this.values[swapCandidate]
this.values[swapCandidate] = node
this.bubbleDown(swapCandidate)
}
return
}
}
export class SafeArray<T> {
private array: Array<T>;
private addQueue: Array<T>];
private removeQueue: Array<T>;
public constructor(items?: Array<T> ) {
this.array = items || [];
this.addQueue = [];
this.removeQueue = [];
}
public get isEmpty(): boolean {
return this.array.length + this.addQueue.length === 0;
}
public get length(): number {
return this.array.length + this.addQueue.length - this.removeQueue.length;
}
public add(item: T): void {
this.addQueue.push(item);
}
public remove(item: T): void {
this.removeQueue.push(item);
}
public filter(predicate: (item: T) => boolean): Array<T> {
const result: Array<T> = [];
this.forEach((item: T) => {
if (predicate(item)) {
result.push(item);
}
});
return result;
}
public forEach(callback: (item: T) => void): void {
this.addQueued();
this.removeQueued();
for (const item of this.array) {
if (this.removeQueue.indexOf(item) === -1) {
callback(item);
}
}
this.removeQueued();
}
private addQueued(): void {
this.array = this.array.concat(this.addQueue);
}
private removeQueued(): void {
this.array = this.array.filter((item: T) => !this.removeQueue.includes(item))
this.removeQueue = [];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment