🎯 Рекомендуемые коллекции
Балансированные коллекции примеров кода из различных категорий, которые вы можете исследовать
Примеры Структур Данных Web TypeScript
Примеры структур данных Web TypeScript включая операции с массивами, хеш-таблицы и связные списки
💻 Операции с Массивами typescript
🟢 simple
⭐⭐
Полная манипуляция массивами включая операции CRUD, сортировку, поиск, фильтрацию и трансформацию
⏱️ 30 min
🏷️ typescript, web, data structures, arrays
Prerequisites:
Basic TypeScript
// Web TypeScript Array Operations Examples
// Comprehensive array manipulation and processing
// 1. Basic Array Operations
class ArrayOperations<T> {
// Create array
static create<T>(...items: T[]): T[] {
return [...items];
}
// Add element to end
static push<T>(array: T[], item: T): number {
return array.push(item);
}
// Add element to beginning
static unshift<T>(array: T[], item: T): number {
return array.unshift(item);
}
// Remove last element
static pop<T>(array: T[]): T | undefined {
return array.pop();
}
// Remove first element
static shift<T>(array: T[]): T | undefined {
return array.shift();
}
// Get element at index
static get<T>(array: T[], index: number): T | undefined {
return array[index];
}
// Set element at index
static set<T>(array: T[], index: number, item: T): void {
array[index] = item;
}
// Get array length
static length<T>(array: T[]): number {
return array.length;
}
// Check if array is empty
static isEmpty<T>(array: T[]): boolean {
return array.length === 0;
}
// Clear array
static clear<T>(array: T[]): void {
array.length = 0;
}
// Clone array
static clone<T>(array: T[]): T[] {
return [...array];
}
// Merge arrays
static merge<T>(...arrays: T[][]): T[] {
return arrays.flat();
}
// Slice array
static slice<T>(array: T[], start: number, end?: number): T[] {
return array.slice(start, end);
}
// Splice array (remove and insert)
static splice<T>(array: T[], start: number, deleteCount: number, ...items: T[]): T[] {
return array.splice(start, deleteCount, ...items);
}
}
// 2. Array Searching
class ArraySearching<T> {
// Linear search
static linearSearch<T>(array: T[], target: T): number {
for (let i = 0; i < array.length; i++) {
if (array[i] === target) {
return i;
}
}
return -1;
}
// Binary search (requires sorted array)
static binarySearch(array: number[], target: number): number {
let left = 0;
let right = array.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (array[mid] === target) {
return mid;
} else if (array[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
// Find element by predicate
static find<T>(array: T[], predicate: (item: T) => boolean): T | undefined {
return array.find(predicate);
}
// Find index by predicate
static findIndex<T>(array: T[], predicate: (item: T) => boolean): number {
return array.findIndex(predicate);
}
// Find all matching elements
static findAll<T>(array: T[], predicate: (item: T) => boolean): T[] {
return array.filter(predicate);
}
// Find last occurrence
static findLast<T>(array: T[], predicate: (item: T) => boolean): T | undefined {
for (let i = array.length - 1; i >= 0; i--) {
if (predicate(array[i])) {
return array[i];
}
}
return undefined;
}
// Find last index
static findLastIndex<T>(array: T[], predicate: (item: T) => boolean): number {
for (let i = array.length - 1; i >= 0; i--) {
if (predicate(array[i])) {
return i;
}
}
return -1;
}
// Check if element exists
static contains<T>(array: T[], element: T): boolean {
return array.includes(element);
}
// Count occurrences
static countOccurrences<T>(array: T[], element: T): number {
return array.filter(item => item === element).length;
}
}
// 3. Array Sorting
class ArraySorting<T> {
// Bubble sort
static bubbleSort(array: number[]): number[] {
const result = [...array];
const n = result.length;
for (let i = 0; i < n - 1; i++) {
for (let j = 0; j < n - i - 1; j++) {
if (result[j] > result[j + 1]) {
[result[j], result[j + 1]] = [result[j + 1], result[j]];
}
}
}
return result;
}
// Selection sort
static selectionSort(array: number[]): number[] {
const result = [...array];
const n = result.length;
for (let i = 0; i < n - 1; i++) {
let minIndex = i;
for (let j = i + 1; j < n; j++) {
if (result[j] < result[minIndex]) {
minIndex = j;
}
}
[result[i], result[minIndex]] = [result[minIndex], result[i]];
}
return result;
}
// Insertion sort
static insertionSort(array: number[]): number[] {
const result = [...array];
for (let i = 1; i < result.length; i++) {
const key = result[i];
let j = i - 1;
while (j >= 0 && result[j] > key) {
result[j + 1] = result[j];
j--;
}
result[j + 1] = key;
}
return result;
}
// Merge sort
static mergeSort(array: number[]): number[] {
if (array.length <= 1) {
return array;
}
const mid = Math.floor(array.length / 2);
const left = this.mergeSort(array.slice(0, mid));
const right = this.mergeSort(array.slice(mid));
return this.merge(left, right);
}
private static merge(left: number[], right: number[]): number[] {
const result: number[] = [];
let leftIndex = 0;
let rightIndex = 0;
while (leftIndex < left.length && rightIndex < right.length) {
if (left[leftIndex] <= right[rightIndex]) {
result.push(left[leftIndex++]);
} else {
result.push(right[rightIndex++]);
}
}
return result.concat(left.slice(leftIndex)).concat(right.slice(rightIndex));
}
// Quick sort
static quickSort(array: number[]): number[] {
if (array.length <= 1) {
return array;
}
const pivot = array[Math.floor(array.length / 2)];
const left = array.filter(x => x < pivot);
const middle = array.filter(x => x === pivot);
const right = array.filter(x => x > pivot);
return [...this.quickSort(left), ...middle, ...this.quickSort(right)];
}
// Sort with custom comparator
static sortBy<T>(array: T[], comparator: (a: T, b: T) => number): T[] {
return [...array].sort(comparator);
}
// Sort numbers ascending
static sortAscending(array: number[]): number[] {
return [...array].sort((a, b) => a - b);
}
// Sort numbers descending
static sortDescending(array: number[]): number[] {
return [...array].sort((a, b) => b - a);
}
// Sort by property
static sortByProperty<T>(array: T[], property: keyof T): T[] {
return [...array].sort((a, b) => {
const aValue = a[property];
const bValue = b[property];
if (aValue < bValue) return -1;
if (aValue > bValue) return 1;
return 0;
});
}
}
// 4. Array Filtering
class ArrayFiltering<T> {
// Filter by predicate
static filter<T>(array: T[], predicate: (item: T) => boolean): T[] {
return array.filter(predicate);
}
// Filter unique elements
static unique<T>(array: T[]): T[] {
return Array.from(new Set(array));
}
// Filter out null/undefined
static filterNotNull<T>(array: (T | null | undefined)[]): T[] {
return array.filter((item): item is T => item !== null && item !== undefined);
}
// Filter by type
static filterByType<T, U extends T>(array: T[], typeGuard: (item: T) => item is U): U[] {
return array.filter(typeGuard);
}
// Partition array
static partition<T>(array: T[], predicate: (item: T) => boolean): [T[], T[]] {
const truthy: T[] = [];
const falsy: T[] = [];
for (const item of array) {
if (predicate(item)) {
truthy.push(item);
} else {
falsy.push(item);
}
}
return [truthy, falsy];
}
// Remove elements
static remove<T>(array: T[], elementsToRemove: T[]): T[] {
const removeSet = new Set(elementsToRemove);
return array.filter(item => !removeSet.has(item));
}
// Remove at indices
static removeAt<T>(array: T[], indices: number[]): T[] {
const indexSet = new Set(indices);
return array.filter((_, index) => !indexSet.has(index));
}
}
// 5. Array Transformation
class ArrayTransformation<T> {
// Map array
static map<T, U>(array: T[], mapper: (item: T, index: number) => U): U[] {
return array.map(mapper);
}
// Flat map
static flatMap<T, U>(array: T[], mapper: (item: T) => U[]): U[] {
return array.flatMap(mapper);
}
// Reduce array
static reduce<T, U>(array: T[], reducer: (acc: U, item: T, index: number) => U, initialValue: U): U {
return array.reduce(reducer, initialValue);
}
// Reverse array
static reverse<T>(array: T[]): T[] {
return [...array].reverse();
}
// Shuffle array
static shuffle<T>(array: T[]): T[] {
const result = [...array];
for (let i = result.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[result[i], result[j]] = [result[j], result[i]];
}
return result;
}
// Flatten nested array
static flatten<T>(array: T[][]): T[] {
return array.flat();
}
// Group by key
static groupBy<T>(array: T[], keyGetter: (item: T) => string): Record<string, T[]> {
return array.reduce((groups, item) => {
const key = keyGetter(item);
if (!groups[key]) {
groups[key] = [];
}
groups[key].push(item);
return groups;
}, {} as Record<string, T[]>);
}
// Chunk array
static chunk<T>(array: T[], size: number): T[][] {
const chunks: T[][] = [];
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size));
}
return chunks;
}
// Zip arrays
static zip<T, U>(array1: T[], array2: U[]): [T, U][] {
const length = Math.min(array1.length, array2.length);
const result: [T, U][] = [];
for (let i = 0; i < length; i++) {
result.push([array1[i], array2[i]]);
}
return result;
}
// Rotate array
static rotate<T>(array: T[], positions: number): T[] {
const normalizedPositions = positions % array.length;
return [
...array.slice(normalizedPositions),
...array.slice(0, normalizedPositions)
];
}
}
// 6. Array Aggregation
class ArrayAggregation<T> {
// Sum numbers
static sum(array: number[]): number {
return array.reduce((sum, num) => sum + num, 0);
}
// Average
static average(array: number[]): number {
if (array.length === 0) return 0;
return this.sum(array) / array.length;
}
// Min value
static min(array: number[]): number | undefined {
return array.reduce((min, current) => current < min ? current : min, array[0]);
}
// Max value
static max(array: number[]): number | undefined {
return array.reduce((max, current) => current > max ? current : max, array[0]);
}
// Min and max
static minMax(array: number[]): { min: number; max: number } | undefined {
if (array.length === 0) return undefined;
let min = array[0];
let max = array[0];
for (const num of array) {
if (num < min) min = num;
if (num > max) max = num;
}
return { min, max };
}
// Product
static product(array: number[]): number {
return array.reduce((prod, num) => prod * num, 1);
}
// Count by predicate
static count<T>(array: T[], predicate: (item: T) => boolean): number {
return array.filter(predicate).length;
}
// Frequency map
static frequency<T>(array: T[]): Map<T, number> {
const freq = new Map<T, number>();
for (const item of array) {
freq.set(item, (freq.get(item) || 0) + 1);
}
return freq;
}
// Most frequent
static mostFrequent<T>(array: T[]): T | undefined {
const freq = this.frequency(array);
let maxCount = 0;
let mostFrequent: T | undefined;
for (const [item, count] of freq.entries()) {
if (count > maxCount) {
maxCount = count;
mostFrequent = item;
}
}
return mostFrequent;
}
}
// 7. Array Set Operations
class ArraySetOperations<T> {
// Union
static union(array1: T[], array2: T[]): T[] {
return Array.from(new Set([...array1, ...array2]));
}
// Intersection
static intersection(array1: T[], array2: T[]): T[] {
const set2 = new Set(array2);
return array1.filter(item => set2.has(item));
}
// Difference
static difference(array1: T[], array2: T[]): T[] {
const set2 = new Set(array2);
return array1.filter(item => !set2.has(item));
}
// Symmetric difference
static symmetricDifference(array1: T[], array2: T[]): T[] {
const set1 = new Set(array1);
const set2 = new Set(array2);
return [
...array1.filter(item => !set2.has(item)),
...array2.filter(item => !set1.has(item))
];
}
// Is subset
static isSubset(array1: T[], array2: T[]): boolean {
const set2 = new Set(array2);
return array1.every(item => set2.has(item));
}
// Is superset
static isSuperset(array1: T[], array2: T[]): boolean {
return this.isSubset(array2, array1);
}
}
// Usage Examples
async function demonstrateArrayOperations() {
console.log('=== Web TypeScript Array Operations Examples ===\n');
// 1. Basic operations
console.log('--- 1. Basic Operations ---');
const arr = [1, 2, 3];
ArrayOperations.push(arr, 4);
console.log(`After push: ${arr}`);
ArrayOperations.unshift(arr, 0);
console.log(`After unshift: ${arr}`);
console.log(`Pop: ${ArrayOperations.pop(arr)}`);
console.log(`Shift: ${ArrayOperations.shift(arr)}`);
const merged = ArrayOperations.merge([1, 2], [3, 4], [5]);
console.log(`Merged: ${merged}`);
// 2. Searching
console.log('\n--- 2. Searching ---');
const numbers = [5, 3, 8, 1, 9, 2];
console.log(`Array: ${numbers}`);
console.log(`Linear search 8: index ${ArraySearching.linearSearch(numbers, 8)}`);
const sorted = [1, 3, 5, 7, 9, 11];
console.log(`Binary search 7: index ${ArraySearching.binarySearch(sorted, 7)}`);
console.log(`Contains 5: ${ArraySearching.contains(numbers, 5)}`);
console.log(`Count of 3: ${ArraySearching.countOccurrences([...numbers, 3], 3)}`);
// 3. Sorting
console.log('\n--- 3. Sorting ---');
const unsorted = [64, 34, 25, 12, 22, 11, 90];
console.log(`Original: ${unsorted}`);
console.log(`Bubble sort: ${ArraySorting.bubbleSort(unsorted)}`);
console.log(`Quick sort: ${ArraySorting.quickSort(unsorted)}`);
console.log(`Ascending: ${ArraySorting.sortAscending(unsorted)}`);
console.log(`Descending: ${ArraySorting.sortDescending(unsorted)}`);
// 4. Filtering
console.log('\n--- 4. Filtering ---');
const mixed = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4];
console.log(`Original: ${mixed}`);
console.log(`Unique: ${ArrayFiltering.unique(mixed)}`);
const [even, odd] = ArrayFiltering.partition(mixed, n => n % 2 === 0);
console.log(`Even: ${even}`);
console.log(`Odd: ${odd}`);
// 5. Transformation
console.log('\n--- 5. Transformation ---');
const nums = [1, 2, 3, 4, 5];
console.log(`Original: ${nums}`);
console.log(`Doubled: ${ArrayTransformation.map(nums, n => n * 2)}`);
console.log(`Reversed: ${ArrayTransformation.reverse(nums)}`);
console.log(`Shuffled: ${ArrayTransformation.shuffle(nums)}`);
console.log(`Chunked (2): ${ArrayTransformation.chunk(nums, 2).map(c => `[${c}]`).join(', ')}`);
// 6. Aggregation
console.log('\n--- 6. Aggregation ---');
const values = [10, 20, 30, 40, 50];
console.log(`Array: ${values}`);
console.log(`Sum: ${ArrayAggregation.sum(values)}`);
console.log(`Average: ${ArrayAggregation.average(values)}`);
console.log(`Min: ${ArrayAggregation.min(values)}`);
console.log(`Max: ${ArrayAggregation.max(values)}`);
console.log(`Min/Max: ${JSON.stringify(ArrayAggregation.minMax(values))}`);
// 7. Set operations
console.log('\n--- 7. Set Operations ---');
const set1 = [1, 2, 3, 4];
const set2 = [3, 4, 5, 6];
console.log(`Set 1: ${set1}`);
console.log(`Set 2: ${set2}`);
console.log(`Union: ${ArraySetOperations.union(set1, set2)}`);
console.log(`Intersection: ${ArraySetOperations.intersection(set1, set2)}`);
console.log(`Difference: ${ArraySetOperations.difference(set1, set2)}`);
console.log(`Symmetric diff: ${ArraySetOperations.symmetricDifference(set1, set2)}`);
// 8. Group by
console.log('\n--- 8. Group By ---');
const people = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 25 }
];
const grouped = ArrayTransformation.groupBy(people, p => String(p.age));
console.log('Grouped by age:', Object.entries(grouped).map(([k, v]) => `${k}: [${v.map(p => p.name).join(', ')}]`).join(', '));
console.log('\n=== All Array Operations Examples Completed ===');
}
// Export classes
export { ArrayOperations, ArraySearching, ArraySorting, ArrayFiltering, ArrayTransformation, ArrayAggregation, ArraySetOperations };
export { demonstrateArrayOperations };
💻 Хеш-таблица typescript
🟡 intermediate
⭐⭐⭐
Реализовать и использовать хеш-таблицы для эффективного хранения и извлечения ключ-значение с обработкой коллизий
⏱️ 30 min
🏷️ typescript, web, data structures, hash map
Prerequisites:
Intermediate TypeScript
// Web TypeScript Hash Map Examples
// Custom hash map implementation with collision handling
// 1. Basic Hash Map Entry
class HashMapEntry<K, V> {
key: K;
value: V;
next: HashMapEntry<K, V> | null;
constructor(key: K, value: V) {
this.key = key;
this.value = value;
this.next = null;
}
}
// 2. Hash Map Implementation
class HashMap<K, V> {
private buckets: Array<HashMapEntry<K, V> | null>;
private size: number;
private capacity: number;
private loadFactor: number;
constructor(initialCapacity: number = 16, loadFactor: number = 0.75) {
this.capacity = initialCapacity;
this.loadFactor = loadFactor;
this.size = 0;
this.buckets = new Array(this.capacity).fill(null);
}
// Hash function
private hash(key: K): number {
const keyString = String(key);
let hash = 0;
for (let i = 0; i < keyString.length; i++) {
hash = (hash << 5) - hash + keyString.charCodeAt(i);
hash = hash | 0; // Convert to 32-bit integer
}
return Math.abs(hash) % this.capacity;
}
// Put key-value pair
put(key: K, value: V): void {
const index = this.hash(key);
let entry = this.buckets[index];
// Check if key already exists
while (entry !== null) {
if (entry.key === key) {
entry.value = value;
return;
}
entry = entry.next;
}
// Add new entry at the beginning
const newEntry = new HashMapEntry(key, value);
newEntry.next = this.buckets[index];
this.buckets[index] = newEntry;
this.size++;
// Resize if needed
if (this.size / this.capacity >= this.loadFactor) {
this.resize();
}
}
// Get value by key
get(key: K): V | undefined {
const index = this.hash(key);
let entry = this.buckets[index];
while (entry !== null) {
if (entry.key === key) {
return entry.value;
}
entry = entry.next;
}
return undefined;
}
// Remove key-value pair
remove(key: K): boolean {
const index = this.hash(key);
let entry = this.buckets[index];
let prev: HashMapEntry<K, V> | null = null;
while (entry !== null) {
if (entry.key === key) {
if (prev === null) {
this.buckets[index] = entry.next;
} else {
prev.next = entry.next;
}
this.size--;
return true;
}
prev = entry;
entry = entry.next;
}
return false;
}
// Check if key exists
containsKey(key: K): boolean {
return this.get(key) !== undefined;
}
// Check if value exists
containsValue(value: V): boolean {
for (const bucket of this.buckets) {
let entry = bucket;
while (entry !== null) {
if (entry.value === value) {
return true;
}
entry = entry.next;
}
}
return false;
}
// Get all keys
keys(): K[] {
const keys: K[] = [];
for (const bucket of this.buckets) {
let entry = bucket;
while (entry !== null) {
keys.push(entry.key);
entry = entry.next;
}
}
return keys;
}
// Get all values
values(): V[] {
const values: V[] = [];
for (const bucket of this.buckets) {
let entry = bucket;
while (entry !== null) {
values.push(entry.value);
entry = entry.next;
}
}
return values;
}
// Get all entries
entries(): Array<[K, V]> {
const entries: Array<[K, V]> = [];
for (const bucket of this.buckets) {
let entry = bucket;
while (entry !== null) {
entries.push([entry.key, entry.value]);
entry = entry.next;
}
}
return entries;
}
// Get size
getSize(): number {
return this.size;
}
// Check if empty
isEmpty(): boolean {
return this.size === 0;
}
// Clear hash map
clear(): void {
this.buckets = new Array(this.capacity).fill(null);
this.size = 0;
}
// Resize buckets
private resize(): void {
const oldBuckets = this.buckets;
this.capacity *= 2;
this.buckets = new Array(this.capacity).fill(null);
this.size = 0;
for (const bucket of oldBuckets) {
let entry = bucket;
while (entry !== null) {
this.put(entry.key, entry.value);
entry = entry.next;
}
}
}
// Print hash map (for debugging)
print(): void {
console.log('\n=== Hash Map Contents ===');
console.log(`Size: ${this.size}, Capacity: ${this.capacity}`);
for (let i = 0; i < this.buckets.length; i++) {
const bucket = this.buckets[i];
if (bucket !== null) {
console.log(`Bucket ${i}:`);
let entry = bucket;
while (entry !== null) {
console.log(` ${entry.key} => ${entry.value}`);
entry = entry.next;
}
}
}
}
}
// 3. Multi-Value Map (one key, multiple values)
class MultiValueMap<K, V> {
private map = new Map<K, V[]>();
// Add value for key
put(key: K, value: V): void {
if (!this.map.has(key)) {
this.map.set(key, []);
}
this.map.get(key)!.push(value);
}
// Get all values for key
get(key: K): V[] {
return this.map.get(key) || [];
}
// Remove specific value for key
remove(key: K, value: V): boolean {
const values = this.map.get(key);
if (!values) return false;
const index = values.indexOf(value);
if (index > -1) {
values.splice(index, 1);
if (values.length === 0) {
this.map.delete(key);
}
return true;
}
return false;
}
// Remove all values for key
removeAll(key: K): V[] {
const values = this.map.get(key);
this.map.delete(key);
return values || [];
}
// Get all keys
keys(): K[] {
return Array.from(this.map.keys());
}
// Get all values
values(): V[] {
const allValues: V[] = [];
for (const values of this.map.values()) {
allValues.push(...values);
}
return allValues;
}
// Check if key exists
containsKey(key: K): boolean {
return this.map.has(key);
}
// Get size (number of keys)
size(): number {
return this.map.size;
}
// Clear map
clear(): void {
this.map.clear();
}
}
// 4. LRU Cache (Least Recently Used)
class LRUCache<K, V> {
private capacity: number;
private cache = new Map<K, V>();
constructor(capacity: number) {
this.capacity = capacity;
}
// Get value and move to end (most recently used)
get(key: K): V | undefined {
if (!this.cache.has(key)) {
return undefined;
}
const value = this.cache.get(key)!;
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
// Put value and evict least recently used if at capacity
put(key: K, value: V): void {
if (this.cache.has(key)) {
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, value);
}
// Check if key exists
has(key: K): boolean {
return this.cache.has(key);
}
// Get size
size(): number {
return this.cache.size;
}
// Clear cache
clear(): void {
this.cache.clear();
}
// Get all keys (in order of least to most recently used)
keys(): K[] {
return Array.from(this.cache.keys());
}
}
// 5. Hash Map Utilities
class HashMapUtilities {
// Merge two hash maps
static merge<K, V>(map1: Map<K, V>, map2: Map<K, V>): Map<K, V> {
return new Map([...map1, ...map2]);
}
// Clone hash map
static clone<K, V>(map: Map<K, V>): Map<K, V> {
return new Map(map);
}
// Invert hash map (swap keys and values)
static invert<K, V>(map: Map<K, V>): Map<V, K> {
const inverted = new Map<V, K>();
for (const [key, value] of map.entries()) {
inverted.set(value, key);
}
return inverted;
}
// Filter hash map by predicate
static filter<K, V>(map: Map<K, V>, predicate: (key: K, value: V) => boolean): Map<K, V> {
const filtered = new Map<K, V>();
for (const [key, value] of map.entries()) {
if (predicate(key, value)) {
filtered.set(key, value);
}
}
return filtered;
}
// Map values
static mapValues<K, V, U>(map: Map<K, V>, mapper: (key: K, value: V) => U): Map<K, U> {
const result = new Map<K, U>();
for (const [key, value] of map.entries()) {
result.set(key, mapper(key, value));
}
return result;
}
// Group array by key function
static groupBy<T, K>(array: T[], keyGetter: (item: T) => K): Map<K, T[]> {
const grouped = new Map<K, T[]>();
for (const item of array) {
const key = keyGetter(item);
if (!grouped.has(key)) {
grouped.set(key, []);
}
grouped.get(key)!.push(item);
}
return grouped;
}
// Count occurrences
static countOccurrences<T>(array: T[]): Map<T, number> {
const counts = new Map<T, number>();
for (const item of array) {
counts.set(item, (counts.get(item) || 0) + 1);
}
return counts;
}
}
// 6. Hash Map Comparison
class HashMapComparison {
// Check if two maps are equal
static equal<K, V>(map1: Map<K, V>, map2: Map<K, V>): boolean {
if (map1.size !== map2.size) {
return false;
}
for (const [key, value] of map1.entries()) {
if (!map2.has(key) || map2.get(key) !== value) {
return false;
}
}
return true;
}
// Get keys present in map1 but not in map2
static keyDifference<K, V>(map1: Map<K, V>, map2: Map<K, V>): Set<K> {
const diff = new Set<K>();
for (const key of map1.keys()) {
if (!map2.has(key)) {
diff.add(key);
}
}
return diff;
}
// Get common keys
static keyIntersection<K, V>(map1: Map<K, V>, map2: Map<K, V>): Set<K> {
const intersection = new Set<K>();
for (const key of map1.keys()) {
if (map2.has(key)) {
intersection.add(key);
}
}
return intersection;
}
}
// Usage Examples
async function demonstrateHashMap() {
console.log('=== Web TypeScript Hash Map Examples ===\n');
// 1. Basic hash map operations
console.log('--- 1. Basic Hash Map ---');
const hashMap = new HashMap<string, number>();
hashMap.put('one', 1);
hashMap.put('two', 2);
hashMap.put('three', 3);
console.log(`Get 'two': ${hashMap.get('two')}`);
console.log(`Contains 'three': ${hashMap.containsKey('three')}`);
console.log(`Keys: ${hashMap.keys().join(', ')}`);
console.log(`Values: ${hashMap.values().join(', ')}`);
console.log(`Size: ${hashMap.getSize()}`);
hashMap.remove('two');
console.log(`After remove 'two', size: ${hashMap.getSize()}`);
// 2. Collision handling
console.log('\n--- 2. Collision Handling ---');
const smallMap = new HashMap<number, string>(4);
// These might hash to same bucket
smallMap.put(1, 'one');
smallMap.put(5, 'five');
smallMap.put(9, 'nine');
console.log(`Get 1: ${smallMap.get(1)}`);
console.log(`Get 5: ${smallMap.get(5)}`);
console.log(`Get 9: ${smallMap.get(9)}`);
smallMap.print();
// 3. Multi-value map
console.log('\n--- 3. Multi-Value Map ---');
const multiMap = new MultiValueMap<string, number>();
multiMap.put('fruits', 1);
multiMap.put('fruits', 2);
multiMap.put('fruits', 3);
multiMap.put('vegetables', 10);
console.log(`Fruits: ${multiMap.get('fruits').join(', ')}`);
console.log(`Vegetables: ${multiMap.get('vegetables').join(', ')}`);
multiMap.remove('fruits', 2);
console.log(`After removing 2 from fruits: ${multiMap.get('fruits').join(', ')}`);
// 4. LRU Cache
console.log('\n--- 4. LRU Cache ---');
const lru = new LRUCache<string, number>(3);
lru.put('a', 1);
lru.put('b', 2);
lru.put('c', 3);
console.log(`Cache keys: ${lru.keys().join(', ')}`);
lru.put('d', 4); // 'a' should be evicted
console.log(`After adding 'd': ${lru.keys().join(', ')}`);
console.log(`Has 'a': ${lru.has('a')}`);
console.log(`Get 'b': ${lru.get('b')}`);
console.log(`After getting 'b': ${lru.keys().join(', ')}`);
// 5. Hash map utilities
console.log('\n--- 5. Hash Map Utilities ---');
const map1 = new Map([['a', 1], ['b', 2], ['c', 3]]);
const map2 = new Map([['c', 30], ['d', 4]]);
const merged = HashMapUtilities.merge(map1, map2);
console.log(`Merged: ${Array.from(merged.entries()).map(([k, v]) => `${k}:${v}`).join(', ')}`);
const filtered = HashMapUtilities.filter(map1, (k, v) => v > 1);
console.log(`Filtered (> 1): ${Array.from(filtered.entries()).map(([k, v]) => `${k}:${v}`).join(', ')}`);
const numbers = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4];
const counts = HashMapUtilities.countOccurrences(numbers);
console.log(`Occurrences: ${Array.from(counts.entries()).map(([k, v]) => `${k}:${v}`).join(', ')}`);
// 6. Group by
console.log('\n--- 6. Group By ---');
const people = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 25 },
{ name: 'David', age: 30 }
];
const grouped = HashMapUtilities.groupBy(people, p => p.age);
console.log('Grouped by age:');
for (const [age, persons] of grouped.entries()) {
console.log(` ${age}: ${persons.map(p => p.name).join(', ')}`);
}
// 7. Hash map comparison
console.log('\n--- 7. Hash Map Comparison ---');
const mapA = new Map([['x', 1], ['y', 2]]);
const mapB = new Map([['x', 1], ['y', 2]]);
const mapC = new Map([['x', 1], ['z', 2]]);
console.log(`A equals B: ${HashMapComparison.equal(mapA, mapB)}`);
console.log(`A equals C: ${HashMapComparison.equal(mapA, mapC)}`);
const diff = HashMapComparison.keyDifference(mapA, mapC);
console.log(`A - C keys: ${Array.from(diff).join(', ')}`);
const intersection = HashMapComparison.keyIntersection(mapA, mapC);
console.log(`A ∩ C keys: ${Array.from(intersection).join(', ')}`);
console.log('\n=== All Hash Map Examples Completed ===');
}
// Export classes
export { HashMap, HashMapEntry, MultiValueMap, LRUCache, HashMapUtilities, HashMapComparison };
export { demonstrateHashMap };
💻 Связные Списки typescript
🟡 intermediate
⭐⭐⭐
Создавать и манипулировать односвязными и двусвязными списками с операциями обхода и модификации
⏱️ 30 min
🏷️ typescript, web, data structures, linked list
Prerequisites:
Intermediate TypeScript, Pointers and References
// Web TypeScript Linked List Examples
// Singly and doubly linked list implementations
// 1. Singly Linked List Node
class ListNode<T> {
value: T;
next: ListNode<T> | null;
constructor(value: T) {
this.value = value;
this.next = null;
}
}
// 2. Singly Linked List
class LinkedList<T> {
private head: ListNode<T> | null = null;
private tail: ListNode<T> | null = null;
private length: number = 0;
// Add to end
append(value: T): void {
const newNode = new ListNode(value);
if (!this.head) {
this.head = newNode;
this.tail = newNode;
} else {
this.tail!.next = newNode;
this.tail = newNode;
}
this.length++;
}
// Add to beginning
prepend(value: T): void {
const newNode = new ListNode(value);
if (!this.head) {
this.head = newNode;
this.tail = newNode;
} else {
newNode.next = this.head;
this.head = newNode;
}
this.length++;
}
// Insert at index
insert(index: number, value: T): boolean {
if (index < 0 || index > this.length) {
return false;
}
if (index === 0) {
this.prepend(value);
return true;
}
if (index === this.length) {
this.append(value);
return true;
}
const newNode = new ListNode(value);
const current = this.getNodeAt(index - 1);
if (current) {
newNode.next = current.next;
current.next = newNode;
this.length++;
return true;
}
return false;
}
// Remove at index
removeAt(index: number): T | null {
if (index < 0 || index >= this.length) {
return null;
}
if (index === 0) {
const value = this.head!.value;
this.head = this.head!.next;
if (!this.head) {
this.tail = null;
}
this.length--;
return value;
}
const prev = this.getNodeAt(index - 1);
if (prev && prev.next) {
const value = prev.next.value;
prev.next = prev.next.next;
if (!prev.next) {
this.tail = prev;
}
this.length--;
return value;
}
return null;
}
// Remove first occurrence of value
remove(value: T): boolean {
if (!this.head) {
return false;
}
if (this.head.value === value) {
this.head = this.head.next;
if (!this.head) {
this.tail = null;
}
this.length--;
return true;
}
let current = this.head;
while (current.next) {
if (current.next.value === value) {
current.next = current.next.next;
if (!current.next) {
this.tail = current;
}
this.length--;
return true;
}
current = current.next;
}
return false;
}
// Get value at index
get(index: number): T | null {
if (index < 0 || index >= this.length) {
return null;
}
const node = this.getNodeAt(index);
return node ? node.value : null;
}
// Find index of value
indexOf(value: T): number {
let current = this.head;
let index = 0;
while (current) {
if (current.value === value) {
return index;
}
current = current.next;
index++;
}
return -1;
}
// Check if contains value
contains(value: T): boolean {
return this.indexOf(value) !== -1;
}
// Get size
size(): number {
return this.length;
}
// Check if empty
isEmpty(): boolean {
return this.length === 0;
}
// Clear list
clear(): void {
this.head = null;
this.tail = null;
this.length = 0;
}
// Convert to array
toArray(): T[] {
const result: T[] = [];
let current = this.head;
while (current) {
result.push(current.value);
current = current.next;
}
return result;
}
// Reverse list
reverse(): void {
let prev: ListNode<T> | null = null;
let current = this.head;
this.tail = this.head;
while (current) {
const next = current.next;
current.next = prev;
prev = current;
current = next;
}
this.head = prev;
}
// Get node at index (private helper)
private getNodeAt(index: number): ListNode<T> | null {
let current = this.head;
let count = 0;
while (current && count < index) {
current = current.next;
count++;
}
return current;
}
// Print list
print(): void {
const values = this.toArray();
console.log(`[${values.join(' -> ')}]`);
}
}
// 3. Doubly Linked List Node
class DoublyListNode<T> {
value: T;
next: DoublyListNode<T> | null;
prev: DoublyListNode<T> | null;
constructor(value: T) {
this.value = value;
this.next = null;
this.prev = null;
}
}
// 4. Doubly Linked List
class DoublyLinkedList<T> {
private head: DoublyListNode<T> | null = null;
private tail: DoublyListNode<T> | null = null;
private length: number = 0;
// Add to end
append(value: T): void {
const newNode = new DoublyListNode(value);
if (!this.head) {
this.head = newNode;
this.tail = newNode;
} else {
newNode.prev = this.tail;
this.tail!.next = newNode;
this.tail = newNode;
}
this.length++;
}
// Add to beginning
prepend(value: T): void {
const newNode = new DoublyListNode(value);
if (!this.head) {
this.head = newNode;
this.tail = newNode;
} else {
newNode.next = this.head;
this.head.prev = newNode;
this.head = newNode;
}
this.length++;
}
// Insert at index
insert(index: number, value: T): boolean {
if (index < 0 || index > this.length) {
return false;
}
if (index === 0) {
this.prepend(value);
return true;
}
if (index === this.length) {
this.append(value);
return true;
}
const newNode = new DoublyListNode(value);
const current = this.getNodeAt(index);
if (current) {
newNode.prev = current.prev;
newNode.next = current;
current.prev!.next = newNode;
current.prev = newNode;
this.length++;
return true;
}
return false;
}
// Remove at index
removeAt(index: number): T | null {
if (index < 0 || index >= this.length) {
return null;
}
const node = this.getNodeAt(index);
if (!node) {
return null;
}
if (node.prev) {
node.prev.next = node.next;
} else {
this.head = node.next;
}
if (node.next) {
node.next.prev = node.prev;
} else {
this.tail = node.prev;
}
this.length--;
return node.value;
}
// Get value at index
get(index: number): T | null {
if (index < 0 || index >= this.length) {
return null;
}
const node = this.getNodeAt(index);
return node ? node.value : null;
}
// Find index of value
indexOf(value: T): number {
let current = this.head;
let index = 0;
while (current) {
if (current.value === value) {
return index;
}
current = current.next;
index++;
}
return -1;
}
// Get size
size(): number {
return this.length;
}
// Check if empty
isEmpty(): boolean {
return this.length === 0;
}
// Clear list
clear(): void {
this.head = null;
this.tail = null;
this.length = 0;
}
// Convert to array
toArray(): T[] {
const result: T[] = [];
let current = this.head;
while (current) {
result.push(current.value);
current = current.next;
}
return result;
}
// Convert to reversed array
toArrayReverse(): T[] {
const result: T[] = [];
let current = this.tail;
while (current) {
result.push(current.value);
current = current.prev;
}
return result;
}
// Get node at index (private helper)
private getNodeAt(index: number): DoublyListNode<T> | null {
// Optimize by starting from closest end
if (index < this.length / 2) {
let current = this.head;
let count = 0;
while (current && count < index) {
current = current.next;
count++;
}
return current;
} else {
let current = this.tail;
let count = this.length - 1;
while (current && count > index) {
current = current.prev;
count--;
}
return current;
}
}
// Print list forward
printForward(): void {
const values = this.toArray();
console.log(`Forward: [${values.join(' <-> ')}]`);
}
// Print list backward
printBackward(): void {
const values = this.toArrayReverse();
console.log(`Backward: [${values.join(' <-> ')}]`);
}
}
// 5. Linked List Utilities
class LinkedListUtilities {
// Detect cycle in linked list
static hasCycle<T>(head: ListNode<T> | null): boolean {
if (!head) {
return false;
}
let slow = head;
let fast = head;
while (fast && fast.next) {
slow = slow.next!;
fast = fast.next.next;
if (slow === fast) {
return true;
}
}
return false;
}
// Find middle node
static findMiddle<T>(head: ListNode<T> | null): ListNode<T> | null {
if (!head) {
return null;
}
let slow = head;
let fast = head;
while (fast && fast.next) {
slow = slow.next!;
fast = fast.next.next;
}
return slow;
}
// Reverse linked list
static reverse<T>(head: ListNode<T> | null): ListNode<T> | null {
let prev: ListNode<T> | null = null;
let current = head;
while (current) {
const next = current.next;
current.next = prev;
prev = current;
current = next;
}
return prev;
}
// Merge two sorted lists
static mergeSortedLists<T>(
list1: ListNode<T> | null,
list2: ListNode<T> | null,
compare: (a: T, b: T) => number
): ListNode<T> | null {
const dummy = new ListNode<T>(null as T);
let current = dummy;
while (list1 && list2) {
if (compare(list1.value, list2.value) <= 0) {
current.next = list1;
list1 = list1.next;
} else {
current.next = list2;
list2 = list2.next;
}
current = current.next;
}
current.next = list1 || list2;
return dummy.next;
}
// Create linked list from array
static fromArray<T>(values: T[]): LinkedList<T> {
const list = new LinkedList<T>();
for (const value of values) {
list.append(value);
}
return list;
}
}
// 6. Linked List Algorithms
class LinkedListAlgorithms {
// Check if palindrome
static isPalindrome<T>(list: LinkedList<T>): boolean {
const values = list.toArray();
const reversed = [...values].reverse();
return JSON.stringify(values) === JSON.stringify(reversed);
}
// Remove nth node from end
static removeNthFromEnd<T>(list: LinkedList<T>, n: number): boolean {
const index = list.size() - n;
if (index < 0) {
return false;
}
return list.removeAt(index) !== null;
}
// Swap nodes in pairs
static swapPairs<T>(list: LinkedList<T>): void {
for (let i = 0; i < list.size() - 1; i += 2) {
const first = list.get(i);
const second = list.get(i + 1);
if (first !== null && second !== null) {
list.removeAt(i);
list.removeAt(i);
list.insert(i, second);
list.insert(i + 1, first);
}
}
}
// Rotate list to the right by k places
static rotateRight<T>(list: LinkedList<T>, k: number): void {
if (list.isEmpty() || k === 0) {
return;
}
const size = list.size();
k = k % size;
if (k === 0) {
return;
}
// Get the split point
const splitIndex = size - k;
// Create new list with rotated elements
const values = list.toArray();
const rotated = [
...values.slice(splitIndex),
...values.slice(0, splitIndex)
];
list.clear();
for (const value of rotated) {
list.append(value);
}
}
}
// Usage Examples
async function demonstrateLinkedList() {
console.log('=== Web TypeScript Linked List Examples ===\n');
// 1. Singly linked list basics
console.log('--- 1. Singly Linked List ---');
const list = new LinkedList<number>();
list.append(1);
list.append(2);
list.append(3);
list.prepend(0);
console.log('After adding elements:');
list.print();
console.log(`Get index 2: ${list.get(2)}`);
console.log(`Index of 2: ${list.indexOf(2)}`);
console.log(`Contains 3: ${list.contains(3)}`);
console.log(`Size: ${list.size()}`);
list.insert(2, 100);
console.log('After inserting 100 at index 2:');
list.print();
list.removeAt(2);
console.log('After removing index 2:');
list.print();
list.reverse();
console.log('After reversing:');
list.print();
// 2. Doubly linked list
console.log('\n--- 2. Doubly Linked List ---');
const doublyList = new DoublyLinkedList<string>();
doublyList.append('first');
doublyList.append('second');
doublyList.append('third');
doublyList.prepend('zero');
console.log('Forward:');
doublyList.printForward();
console.log('Backward:');
doublyList.printBackward();
doublyList.insert(2, 'middle');
console.log('After inserting at index 2:');
doublyList.printForward();
console.log(`Removed at index 2: ${doublyList.removeAt(2)}`);
doublyList.printForward();
// 3. Linked list utilities
console.log('\n--- 3. Linked List Utilities ---');
// Create a list for utilities
const utilList = LinkedListUtilities.fromArray([1, 2, 3, 4, 5]);
console.log('Created from array [1,2,3,4,5]:');
utilList.print();
// Find middle
const middle = LinkedListUtilities.findMiddle(utilList['head'] || null);
console.log(`Middle node value: ${middle?.value}`);
// Reverse
const reversed = LinkedListUtilities.reverse(utilList['head'] || null);
console.log('Reversed:');
let current = reversed;
while (current) {
console.log(current.value + ' -> ');
current = current.next;
}
// 4. Linked list algorithms
console.log('\n--- 4. Linked List Algorithms ---');
// Palindrome check
const palindromeList = LinkedListUtilities.fromArray([1, 2, 3, 2, 1]);
console.log(`Is [1,2,3,2,1] palindrome: ${LinkedListAlgorithms.isPalindrome(palindromeList)}`);
const nonPalindromeList = LinkedListUtilities.fromArray([1, 2, 3, 4, 5]);
console.log(`Is [1,2,3,4,5] palindrome: ${LinkedListAlgorithms.isPalindrome(nonPalindromeList)}`);
// Remove nth from end
const removeList = LinkedListUtilities.fromArray([1, 2, 3, 4, 5]);
console.log('Before removing 2nd from end:');
removeList.print();
LinkedListAlgorithms.removeNthFromEnd(removeList, 2);
console.log('After removing 2nd from end:');
removeList.print();
// Rotate right
const rotateList = LinkedListUtilities.fromArray([1, 2, 3, 4, 5]);
console.log('Before rotating right by 2:');
rotateList.print();
LinkedListAlgorithms.rotateRight(rotateList, 2);
console.log('After rotating right by 2:');
rotateList.print();
// Swap pairs
const swapList = LinkedListUtilities.fromArray([1, 2, 3, 4, 5]);
console.log('Before swapping pairs:');
swapList.print();
LinkedListAlgorithms.swapPairs(swapList);
console.log('After swapping pairs:');
swapList.print();
// 5. Conversion
console.log('\n--- 5. Conversion ---');
const arr = [10, 20, 30, 40, 50];
const listFromArray = LinkedListUtilities.fromArray(arr);
console.log(`Array: [${arr.join(', ')}]`);
console.log('Linked list:');
listFromArray.print();
const backToArray = listFromArray.toArray();
console.log(`Back to array: [${backToArray.join(', ')}]`);
console.log('\n=== All Linked List Examples Completed ===');
}
// Export classes
export { ListNode, LinkedList, DoublyListNode, DoublyLinkedList, LinkedListUtilities, LinkedListAlgorithms };
export { demonstrateLinkedList };