Remove technical documentation and testing files; add ESLint configuration and CI scripts for build and deployment processes.
This commit is contained in:
493
CLAUDE.md
493
CLAUDE.md
@@ -1,493 +0,0 @@
|
|||||||
# HashMap Implementation - Technical Documentation
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This is a production-ready HashMap implementation in TypeScript that strictly follows OOP SOLID principles and best practices. The implementation uses separate chaining for collision resolution and provides automatic resizing based on load factor.
|
|
||||||
|
|
||||||
## SOLID Principles Implementation
|
|
||||||
|
|
||||||
### 1. Single Responsibility Principle (SRP)
|
|
||||||
|
|
||||||
Each class has one clearly defined responsibility:
|
|
||||||
|
|
||||||
#### `HashMap` (`src/core/HashMap.ts`)
|
|
||||||
- **Responsibility**: Managing the hash table and coordinating operations
|
|
||||||
- **Single Purpose**: Provide efficient key-value storage and retrieval
|
|
||||||
|
|
||||||
#### `HashNode` (`src/models/HashNode.ts`)
|
|
||||||
- **Responsibility**: Storing a single key-value pair and linking to the next node
|
|
||||||
- **Single Purpose**: Data container for collision chains
|
|
||||||
|
|
||||||
#### `DefaultHashFunction` (`src/hash-functions/DefaultHashFunction.ts`)
|
|
||||||
- **Responsibility**: Computing hash values for keys
|
|
||||||
- **Single Purpose**: Convert keys to bucket indices
|
|
||||||
|
|
||||||
#### `NumericHashFunction` (`src/hash-functions/NumericHashFunction.ts`)
|
|
||||||
- **Responsibility**: Optimized hashing for numeric keys
|
|
||||||
- **Single Purpose**: Provide better distribution for numeric data
|
|
||||||
|
|
||||||
### 2. Open/Closed Principle (OCP)
|
|
||||||
|
|
||||||
**Open for Extension, Closed for Modification**
|
|
||||||
|
|
||||||
The implementation is extensible without modifying core code:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Extend functionality by providing custom hash functions
|
|
||||||
class CustomHashFunction implements IHashFunction<string> {
|
|
||||||
hash(key: string, capacity: number): number {
|
|
||||||
// Custom hashing logic
|
|
||||||
return /* computed hash */;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use custom function without modifying HashMap
|
|
||||||
const map = new HashMap<string, number>(16, 0.75, new CustomHashFunction());
|
|
||||||
```
|
|
||||||
|
|
||||||
**Key Design Decisions:**
|
|
||||||
- Hash function is injected via constructor (dependency injection)
|
|
||||||
- New hash strategies can be added without changing HashMap
|
|
||||||
- Generic types allow any key/value types without modification
|
|
||||||
|
|
||||||
### 3. Liskov Substitution Principle (LSP)
|
|
||||||
|
|
||||||
**Subtypes must be substitutable for their base types**
|
|
||||||
|
|
||||||
All implementations properly implement their interfaces:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Any IHashFunction can replace another
|
|
||||||
function createMap<K, V>(hashFn: IHashFunction<K>): IHashMap<K, V> {
|
|
||||||
return new HashMap<K, V>(16, 0.75, hashFn);
|
|
||||||
}
|
|
||||||
|
|
||||||
// All these work identically
|
|
||||||
const map1 = createMap(new DefaultHashFunction());
|
|
||||||
const map2 = createMap(new NumericHashFunction());
|
|
||||||
const map3 = createMap(new CustomHashFunction());
|
|
||||||
```
|
|
||||||
|
|
||||||
**Guarantees:**
|
|
||||||
- All IHashFunction implementations provide correct hash values
|
|
||||||
- HashMap correctly implements IHashMap interface
|
|
||||||
- No unexpected behavior when substituting implementations
|
|
||||||
|
|
||||||
### 4. Interface Segregation Principle (ISP)
|
|
||||||
|
|
||||||
**Clients shouldn't depend on interfaces they don't use**
|
|
||||||
|
|
||||||
The codebase provides focused, minimal interfaces:
|
|
||||||
|
|
||||||
#### `IHashFunction<K>`
|
|
||||||
```typescript
|
|
||||||
interface IHashFunction<K> {
|
|
||||||
hash(key: K, capacity: number): number;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- Single method interface
|
|
||||||
- Only requires hash computation
|
|
||||||
- No unnecessary methods
|
|
||||||
|
|
||||||
#### `IHashMap<K, V>`
|
|
||||||
```typescript
|
|
||||||
interface IHashMap<K, V> {
|
|
||||||
set(key: K, value: V): void;
|
|
||||||
get(key: K): V | undefined;
|
|
||||||
has(key: K): boolean;
|
|
||||||
delete(key: K): boolean;
|
|
||||||
clear(): void;
|
|
||||||
// ... iterator methods
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- Focused on map operations
|
|
||||||
- No coupling to hashing details
|
|
||||||
- Clean separation of concerns
|
|
||||||
|
|
||||||
### 5. Dependency Inversion Principle (DIP)
|
|
||||||
|
|
||||||
**Depend on abstractions, not concretions**
|
|
||||||
|
|
||||||
High-level modules depend on abstractions:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
export class HashMap<K, V> implements IHashMap<K, V> {
|
|
||||||
private readonly hashFunction: IHashFunction<K>; // Depends on abstraction
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
initialCapacity: number = 16,
|
|
||||||
loadFactorThreshold: number = 0.75,
|
|
||||||
hashFunction?: IHashFunction<K> // Inject dependency
|
|
||||||
) {
|
|
||||||
this.hashFunction = hashFunction ?? new DefaultHashFunction<K>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits:**
|
|
||||||
- HashMap doesn't depend on concrete hash implementations
|
|
||||||
- Easy to test with mock hash functions
|
|
||||||
- Can swap hash strategies at runtime
|
|
||||||
- Follows Dependency Injection pattern
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
### Directory Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
src/
|
|
||||||
├── core/ # Core implementations
|
|
||||||
│ └── HashMap.ts # Main HashMap class
|
|
||||||
├── interfaces/ # Contracts and abstractions
|
|
||||||
│ ├── IHashFunction.ts # Hash function interface
|
|
||||||
│ └── IHashMap.ts # HashMap interface
|
|
||||||
├── models/ # Data structures
|
|
||||||
│ └── HashNode.ts # Collision chain node
|
|
||||||
├── hash-functions/ # Hashing strategies
|
|
||||||
│ ├── DefaultHashFunction.ts # General-purpose hashing
|
|
||||||
│ └── NumericHashFunction.ts # Numeric optimization
|
|
||||||
├── examples/ # Usage demonstrations
|
|
||||||
│ ├── basic-usage.ts
|
|
||||||
│ └── custom-hash-function.ts
|
|
||||||
└── index.ts # Public API exports
|
|
||||||
```
|
|
||||||
|
|
||||||
### Design Patterns Used
|
|
||||||
|
|
||||||
#### 1. Strategy Pattern
|
|
||||||
- **Where**: Hash function selection
|
|
||||||
- **Why**: Allows different hashing algorithms to be plugged in
|
|
||||||
- **Implementation**: `IHashFunction` interface with multiple implementations
|
|
||||||
|
|
||||||
#### 2. Iterator Pattern
|
|
||||||
- **Where**: `keys()`, `values()`, `entries()` methods
|
|
||||||
- **Why**: Provides consistent way to traverse the collection
|
|
||||||
- **Implementation**: Generator functions with `IterableIterator<T>`
|
|
||||||
|
|
||||||
#### 3. Dependency Injection
|
|
||||||
- **Where**: Constructor accepts `IHashFunction`
|
|
||||||
- **Why**: Decouples HashMap from specific hash implementations
|
|
||||||
- **Implementation**: Constructor parameter with default
|
|
||||||
|
|
||||||
### Data Structure Design
|
|
||||||
|
|
||||||
#### Collision Resolution: Separate Chaining
|
|
||||||
|
|
||||||
```
|
|
||||||
Buckets Array:
|
|
||||||
[0] -> Node(k1, v1) -> Node(k2, v2) -> null
|
|
||||||
[1] -> null
|
|
||||||
[2] -> Node(k3, v3) -> null
|
|
||||||
[3] -> Node(k4, v4) -> Node(k5, v5) -> Node(k6, v6) -> null
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
**Advantages:**
|
|
||||||
- Simple to implement
|
|
||||||
- No clustering issues
|
|
||||||
- Can handle high load factors
|
|
||||||
- Dynamic growth with chains
|
|
||||||
|
|
||||||
**Trade-offs:**
|
|
||||||
- Extra memory for node references
|
|
||||||
- Cache locality could be better
|
|
||||||
- O(n) worst-case for long chains
|
|
||||||
|
|
||||||
#### Load Factor and Resizing
|
|
||||||
|
|
||||||
**Default Configuration:**
|
|
||||||
- Initial Capacity: 16 buckets
|
|
||||||
- Load Factor Threshold: 0.75
|
|
||||||
|
|
||||||
**Resizing Strategy:**
|
|
||||||
```typescript
|
|
||||||
if (size / capacity >= loadFactorThreshold) {
|
|
||||||
resize(capacity * 2); // Double the capacity
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Why 0.75?**
|
|
||||||
- Good balance between space and time
|
|
||||||
- Keeps chains short on average
|
|
||||||
- Industry standard (used by Java HashMap)
|
|
||||||
|
|
||||||
## Performance Characteristics
|
|
||||||
|
|
||||||
### Time Complexity
|
|
||||||
|
|
||||||
| Operation | Average Case | Worst Case | Notes |
|
|
||||||
|-----------|--------------|------------|-------|
|
|
||||||
| `set(k, v)` | O(1) | O(n) | Worst case if all keys hash to same bucket |
|
|
||||||
| `get(k)` | O(1) | O(n) | Requires traversing collision chain |
|
|
||||||
| `has(k)` | O(1) | O(n) | Same as get |
|
|
||||||
| `delete(k)` | O(1) | O(n) | Requires finding and unlinking node |
|
|
||||||
| `clear()` | O(capacity) | O(capacity) | Must null all bucket references |
|
|
||||||
| `keys()` | O(n) | O(n) | Must visit all entries |
|
|
||||||
| `values()` | O(n) | O(n) | Must visit all entries |
|
|
||||||
| `entries()` | O(n) | O(n) | Must visit all entries |
|
|
||||||
|
|
||||||
### Space Complexity
|
|
||||||
|
|
||||||
- **Storage**: O(n) where n is number of entries
|
|
||||||
- **Overhead**: O(capacity) for buckets array
|
|
||||||
- **Per Entry**: Constant overhead for HashNode
|
|
||||||
|
|
||||||
### Load Factor Impact
|
|
||||||
|
|
||||||
```
|
|
||||||
Load Factor = size / capacity
|
|
||||||
|
|
||||||
Low Load Factor (< 0.5):
|
|
||||||
✓ Fewer collisions
|
|
||||||
✓ Faster operations
|
|
||||||
✗ Wastes memory
|
|
||||||
|
|
||||||
High Load Factor (> 0.9):
|
|
||||||
✓ Better memory usage
|
|
||||||
✗ More collisions
|
|
||||||
✗ Slower operations
|
|
||||||
|
|
||||||
Optimal (0.75):
|
|
||||||
✓ Good balance
|
|
||||||
✓ Reasonable memory usage
|
|
||||||
✓ Good performance
|
|
||||||
```
|
|
||||||
|
|
||||||
## Best Practices Demonstrated
|
|
||||||
|
|
||||||
### 1. Type Safety
|
|
||||||
```typescript
|
|
||||||
// Full generic support
|
|
||||||
const map = new HashMap<string, User>(); // Type-safe
|
|
||||||
map.set("id", user); // ✓ Correct
|
|
||||||
map.set(123, user); // ✗ Type error
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Immutability Where Appropriate
|
|
||||||
```typescript
|
|
||||||
// Read-only properties
|
|
||||||
private readonly hashFunction: IHashFunction<K>;
|
|
||||||
private readonly loadFactorThreshold: number;
|
|
||||||
private readonly initialCapacity: number;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Defensive Programming
|
|
||||||
```typescript
|
|
||||||
// Validate constructor arguments
|
|
||||||
if (initialCapacity <= 0) {
|
|
||||||
throw new Error("Initial capacity must be positive");
|
|
||||||
}
|
|
||||||
if (loadFactorThreshold <= 0 || loadFactorThreshold > 1) {
|
|
||||||
throw new Error("Load factor must be between 0 and 1");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Clear Documentation
|
|
||||||
- Every public method documented with JSDoc
|
|
||||||
- Time complexity noted in comments
|
|
||||||
- Usage examples provided
|
|
||||||
|
|
||||||
### 5. Comprehensive Testing
|
|
||||||
- 32 test cases covering all functionality
|
|
||||||
- Edge cases (null, undefined, empty strings)
|
|
||||||
- Performance tests (1000 entries)
|
|
||||||
- Custom hash function tests
|
|
||||||
|
|
||||||
### 6. Iterator Support
|
|
||||||
```typescript
|
|
||||||
// Makes HashMap usable in for...of loops
|
|
||||||
[Symbol.iterator](): IterableIterator<[K, V]> {
|
|
||||||
return this.entries();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usage
|
|
||||||
for (const [key, value] of map) {
|
|
||||||
console.log(key, value);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7. Separation of Concerns
|
|
||||||
- Hashing logic separated from storage logic
|
|
||||||
- Node structure separated from HashMap
|
|
||||||
- Interfaces defined separately from implementations
|
|
||||||
|
|
||||||
## Advanced Features
|
|
||||||
|
|
||||||
### 1. Custom Hash Functions
|
|
||||||
|
|
||||||
Create domain-specific hash functions:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Case-insensitive string keys
|
|
||||||
class CaseInsensitiveHash implements IHashFunction<string> {
|
|
||||||
hash(key: string, capacity: number): number {
|
|
||||||
return computeHash(key.toLowerCase(), capacity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Composite object keys
|
|
||||||
class PersonHashFunction implements IHashFunction<Person> {
|
|
||||||
hash(person: Person, capacity: number): number {
|
|
||||||
const str = `${person.firstName}:${person.lastName}:${person.age}`;
|
|
||||||
return computeHash(str, capacity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Performance Monitoring
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const map = new HashMap<string, number>();
|
|
||||||
|
|
||||||
// Monitor internal state
|
|
||||||
console.log(`Capacity: ${map.capacity}`);
|
|
||||||
console.log(`Size: ${map.size}`);
|
|
||||||
console.log(`Load Factor: ${map.loadFactor}`);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Bulk Operations
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Efficient bulk insertion
|
|
||||||
const entries: [string, number][] = [
|
|
||||||
["a", 1], ["b", 2], ["c", 3]
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const [key, value] of entries) {
|
|
||||||
map.set(key, value);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Strategy
|
|
||||||
|
|
||||||
### Test Coverage
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun test
|
|
||||||
```
|
|
||||||
|
|
||||||
**Coverage Breakdown:**
|
|
||||||
- Core HashMap: 100% function/line coverage
|
|
||||||
- Hash Functions: 66-87% (edge cases for special values)
|
|
||||||
- Overall: 92% line coverage
|
|
||||||
|
|
||||||
### Test Categories
|
|
||||||
|
|
||||||
1. **Constructor Tests**
|
|
||||||
- Default initialization
|
|
||||||
- Custom parameters
|
|
||||||
- Invalid input validation
|
|
||||||
|
|
||||||
2. **Basic Operations**
|
|
||||||
- Set/Get/Has/Delete
|
|
||||||
- Update existing values
|
|
||||||
- Non-existent keys
|
|
||||||
|
|
||||||
3. **Iteration Tests**
|
|
||||||
- Keys iterator
|
|
||||||
- Values iterator
|
|
||||||
- Entries iterator
|
|
||||||
- forEach callback
|
|
||||||
- for...of loops
|
|
||||||
|
|
||||||
4. **Resizing Tests**
|
|
||||||
- Automatic growth
|
|
||||||
- Data preservation
|
|
||||||
- Load factor triggers
|
|
||||||
|
|
||||||
5. **Edge Cases**
|
|
||||||
- Null values
|
|
||||||
- Undefined values
|
|
||||||
- Empty string keys
|
|
||||||
- Large datasets (1000 entries)
|
|
||||||
|
|
||||||
6. **Custom Hash Functions**
|
|
||||||
- NumericHashFunction
|
|
||||||
- Custom implementations
|
|
||||||
|
|
||||||
## Usage Examples
|
|
||||||
|
|
||||||
### Basic Usage
|
|
||||||
```typescript
|
|
||||||
const scores = new HashMap<string, number>();
|
|
||||||
scores.set("Alice", 95);
|
|
||||||
scores.set("Bob", 87);
|
|
||||||
console.log(scores.get("Alice")); // 95
|
|
||||||
```
|
|
||||||
|
|
||||||
### With TypeScript Interfaces
|
|
||||||
```typescript
|
|
||||||
interface Product {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
price: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const products = new HashMap<number, Product>();
|
|
||||||
products.set(1, { id: 1, name: "Widget", price: 9.99 });
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Configuration
|
|
||||||
```typescript
|
|
||||||
const map = new HashMap<string, number>(
|
|
||||||
32, // Initial capacity
|
|
||||||
0.8, // Load factor threshold
|
|
||||||
customHashFn // Custom hash function
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Comparison with Native Map
|
|
||||||
|
|
||||||
### Advantages of This Implementation
|
|
||||||
|
|
||||||
1. **Educational Value**: Shows internal workings
|
|
||||||
2. **Customizable**: Inject custom hash functions
|
|
||||||
3. **Observable**: Can monitor capacity and load factor
|
|
||||||
4. **Extensible**: Easy to add new features
|
|
||||||
|
|
||||||
### Native Map Advantages
|
|
||||||
|
|
||||||
1. **Performance**: Highly optimized in V8/JSC
|
|
||||||
2. **Battle-tested**: Used in production worldwide
|
|
||||||
3. **Standard API**: Consistent across platforms
|
|
||||||
|
|
||||||
### When to Use Each
|
|
||||||
|
|
||||||
**Use HashMap (this implementation):**
|
|
||||||
- Learning data structures
|
|
||||||
- Need custom hash functions
|
|
||||||
- Want to understand internals
|
|
||||||
- Require specific behavior
|
|
||||||
|
|
||||||
**Use Native Map:**
|
|
||||||
- Production applications
|
|
||||||
- Performance critical paths
|
|
||||||
- Standard use cases
|
|
||||||
- Browser compatibility needs
|
|
||||||
|
|
||||||
## Future Enhancements
|
|
||||||
|
|
||||||
Possible improvements while maintaining SOLID principles:
|
|
||||||
|
|
||||||
1. **Additional Hash Functions**
|
|
||||||
- CryptoHashFunction (secure hashing)
|
|
||||||
- IdentityHashFunction (reference equality)
|
|
||||||
|
|
||||||
2. **Performance Optimizations**
|
|
||||||
- Red-black tree for long chains (like Java 8+)
|
|
||||||
- Dynamic shrinking on deletions
|
|
||||||
|
|
||||||
3. **Additional Features**
|
|
||||||
- Weak key references
|
|
||||||
- Computed values (getOrCompute)
|
|
||||||
- Batch operations
|
|
||||||
|
|
||||||
4. **Observability**
|
|
||||||
- Event listeners for changes
|
|
||||||
- Statistics tracking
|
|
||||||
- Performance metrics
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
This HashMap implementation demonstrates how to build a production-quality data structure while adhering to SOLID principles. The clean architecture makes it maintainable, testable, and extensible. It serves as both a practical tool and an educational resource for understanding hash tables and object-oriented design.
|
|
||||||
|
|
||||||
364
TESTING.md
364
TESTING.md
@@ -1,364 +0,0 @@
|
|||||||
# Testing Documentation
|
|
||||||
|
|
||||||
## Running Tests
|
|
||||||
|
|
||||||
### Run All Tests
|
|
||||||
```bash
|
|
||||||
bun test
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run Tests with Coverage
|
|
||||||
```bash
|
|
||||||
bun test --coverage
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run Tests in Watch Mode
|
|
||||||
```bash
|
|
||||||
bun test --watch
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run Specific Test File
|
|
||||||
```bash
|
|
||||||
bun test tests/HashMap.test.ts
|
|
||||||
bun test tests/HashFunctions.test.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test Structure
|
|
||||||
|
|
||||||
### 1. HashMap Tests (`tests/HashMap.test.ts`)
|
|
||||||
|
|
||||||
#### Constructor Tests (4 tests)
|
|
||||||
- ✅ Creates empty map with default capacity
|
|
||||||
- ✅ Creates map with custom initial capacity
|
|
||||||
- ✅ Throws error for invalid capacity
|
|
||||||
- ✅ Throws error for invalid load factor
|
|
||||||
|
|
||||||
#### Set and Get Tests (4 tests)
|
|
||||||
- ✅ Sets and gets values
|
|
||||||
- ✅ Updates existing values
|
|
||||||
- ✅ Returns undefined for non-existent keys
|
|
||||||
- ✅ Handles multiple key-value pairs
|
|
||||||
|
|
||||||
#### Has Tests (2 tests)
|
|
||||||
- ✅ Returns true for existing keys
|
|
||||||
- ✅ Returns false for non-existent keys
|
|
||||||
|
|
||||||
#### Delete Tests (3 tests)
|
|
||||||
- ✅ Deletes existing keys
|
|
||||||
- ✅ Returns false for non-existent keys
|
|
||||||
- ✅ Handles deletion from collision chains
|
|
||||||
|
|
||||||
#### Clear Tests (1 test)
|
|
||||||
- ✅ Removes all entries
|
|
||||||
|
|
||||||
#### Size Tests (1 test)
|
|
||||||
- ✅ Tracks size correctly through operations
|
|
||||||
|
|
||||||
#### Keys Tests (2 tests)
|
|
||||||
- ✅ Iterates over all keys
|
|
||||||
- ✅ Returns empty iterator for empty map
|
|
||||||
|
|
||||||
#### Values Tests (1 test)
|
|
||||||
- ✅ Iterates over all values
|
|
||||||
|
|
||||||
#### Entries Tests (1 test)
|
|
||||||
- ✅ Iterates over all key-value pairs
|
|
||||||
|
|
||||||
#### ForEach Tests (1 test)
|
|
||||||
- ✅ Executes callback for each entry
|
|
||||||
|
|
||||||
#### Iterable Tests (1 test)
|
|
||||||
- ✅ Works with for...of loops
|
|
||||||
|
|
||||||
#### Resizing Tests (2 tests)
|
|
||||||
- ✅ Resizes when load factor exceeds threshold
|
|
||||||
- ✅ Maintains all entries after resize
|
|
||||||
|
|
||||||
#### Custom Hash Function Tests (2 tests)
|
|
||||||
- ✅ Works with NumericHashFunction
|
|
||||||
- ✅ Works with custom implementations
|
|
||||||
|
|
||||||
#### Edge Cases Tests (5 tests)
|
|
||||||
- ✅ Handles null values
|
|
||||||
- ✅ Handles undefined values
|
|
||||||
- ✅ Handles empty string keys
|
|
||||||
- ✅ Handles numeric keys
|
|
||||||
- ✅ Handles large datasets (1000 entries)
|
|
||||||
|
|
||||||
#### Collision Handling Tests (1 test)
|
|
||||||
- ✅ Handles hash collisions correctly
|
|
||||||
|
|
||||||
#### ToString Tests (1 test)
|
|
||||||
- ✅ Provides readable string representation
|
|
||||||
|
|
||||||
**Total: 32 tests**
|
|
||||||
|
|
||||||
### 2. Hash Functions Tests (`tests/HashFunctions.test.ts`)
|
|
||||||
|
|
||||||
#### DefaultHashFunction Tests
|
|
||||||
|
|
||||||
##### Basic Types (5 tests)
|
|
||||||
- ✅ Hashes string keys
|
|
||||||
- ✅ Hashes number keys (positive, negative, zero, floats)
|
|
||||||
- ✅ Hashes boolean keys
|
|
||||||
- ✅ Hashes null
|
|
||||||
- ✅ Hashes undefined
|
|
||||||
|
|
||||||
##### Object Types (7 tests)
|
|
||||||
- ✅ Hashes simple objects
|
|
||||||
- ✅ Hashes arrays
|
|
||||||
- ✅ Hashes nested objects
|
|
||||||
- ✅ Handles circular references gracefully
|
|
||||||
- ✅ Hashes Date objects
|
|
||||||
- ✅ Hashes RegExp objects
|
|
||||||
- ✅ Hashes Error objects
|
|
||||||
|
|
||||||
##### Special Values (5 tests)
|
|
||||||
- ✅ Hashes empty string
|
|
||||||
- ✅ Hashes empty object
|
|
||||||
- ✅ Hashes empty array
|
|
||||||
- ✅ Hashes symbols
|
|
||||||
- ✅ Hashes bigint
|
|
||||||
|
|
||||||
##### Consistency (3 tests)
|
|
||||||
- ✅ Returns same hash for same key
|
|
||||||
- ✅ Handles different keys
|
|
||||||
- ✅ Handles different capacities
|
|
||||||
|
|
||||||
**Subtotal: 20 tests**
|
|
||||||
|
|
||||||
#### NumericHashFunction Tests
|
|
||||||
|
|
||||||
##### Normal Numbers (6 tests)
|
|
||||||
- ✅ Hashes positive integers
|
|
||||||
- ✅ Hashes negative integers
|
|
||||||
- ✅ Hashes zero
|
|
||||||
- ✅ Hashes floating point numbers
|
|
||||||
- ✅ Hashes very large numbers
|
|
||||||
- ✅ Hashes very small numbers
|
|
||||||
|
|
||||||
##### Special Numeric Values (3 tests)
|
|
||||||
- ✅ Handles Infinity
|
|
||||||
- ✅ Handles negative Infinity
|
|
||||||
- ✅ Handles NaN
|
|
||||||
|
|
||||||
##### Consistency (3 tests)
|
|
||||||
- ✅ Returns same hash for same number
|
|
||||||
- ✅ Handles different capacities
|
|
||||||
- ✅ Distributes numbers evenly
|
|
||||||
|
|
||||||
##### Negative Numbers (2 tests)
|
|
||||||
- ✅ Hashes negative numbers correctly
|
|
||||||
- ✅ Handles absolute values consistently
|
|
||||||
|
|
||||||
**Subtotal: 14 tests**
|
|
||||||
|
|
||||||
**Total: 34 tests**
|
|
||||||
|
|
||||||
## Test Categories by Type
|
|
||||||
|
|
||||||
### Unit Tests
|
|
||||||
All tests are unit tests that test individual components in isolation:
|
|
||||||
- **HashMap operations**: Set, get, has, delete, clear
|
|
||||||
- **Hash functions**: Default and numeric hashing
|
|
||||||
- **Data structures**: Node creation and linking
|
|
||||||
|
|
||||||
### Integration Tests
|
|
||||||
Some tests verify integration between components:
|
|
||||||
- Custom hash function injection
|
|
||||||
- Automatic resizing with rehashing
|
|
||||||
- Iterator integration with for...of loops
|
|
||||||
|
|
||||||
### Edge Case Tests
|
|
||||||
Comprehensive edge case coverage:
|
|
||||||
- Special values: null, undefined, empty strings
|
|
||||||
- Non-finite numbers: Infinity, -Infinity, NaN
|
|
||||||
- Circular object references
|
|
||||||
- Empty collections
|
|
||||||
- Large datasets (1000+ entries)
|
|
||||||
- Collision scenarios
|
|
||||||
|
|
||||||
### Performance Tests
|
|
||||||
- Large dataset handling (1000 entries)
|
|
||||||
- Hash distribution verification
|
|
||||||
- Load factor threshold testing
|
|
||||||
|
|
||||||
## Test Design Principles
|
|
||||||
|
|
||||||
### 1. Comprehensive Coverage
|
|
||||||
Every public method and edge case is tested to achieve 100% line coverage.
|
|
||||||
|
|
||||||
### 2. Clear Test Names
|
|
||||||
Test names follow the pattern: "should [expected behavior] [under condition]"
|
|
||||||
|
|
||||||
### 3. Isolated Tests
|
|
||||||
Each test is independent and doesn't rely on state from other tests.
|
|
||||||
|
|
||||||
### 4. Arrange-Act-Assert Pattern
|
|
||||||
```typescript
|
|
||||||
it("should set and get a value", () => {
|
|
||||||
// Arrange
|
|
||||||
map.set("key", 100);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
const result = map.get("key");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(result).toBe(100);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Edge Case Testing
|
|
||||||
Every special value and error condition is tested:
|
|
||||||
- Boundary values (0, empty, max)
|
|
||||||
- Error conditions (invalid inputs)
|
|
||||||
- Special types (null, undefined, NaN, Infinity)
|
|
||||||
- Complex scenarios (circular references)
|
|
||||||
|
|
||||||
### 6. Behavior-Driven Tests
|
|
||||||
Tests verify behavior, not implementation details:
|
|
||||||
- Focus on what the code does, not how
|
|
||||||
- Test public APIs, not private methods
|
|
||||||
- Verify contracts, not internals
|
|
||||||
|
|
||||||
## Code Coverage Breakdown
|
|
||||||
|
|
||||||
### Line Coverage: 100%
|
|
||||||
Every executable line of code is covered by at least one test.
|
|
||||||
|
|
||||||
### Function Coverage: 83.33%
|
|
||||||
Some private helper functions and constructors show lower coverage due to how Bun calculates coverage, but all their code paths are executed.
|
|
||||||
|
|
||||||
### Branch Coverage: Implicit 100%
|
|
||||||
All conditional branches (if/else, switch, ternary) are covered:
|
|
||||||
- Error handling paths
|
|
||||||
- Special value handling
|
|
||||||
- Collision resolution paths
|
|
||||||
- Resize triggering conditions
|
|
||||||
|
|
||||||
## Coverage Achievements
|
|
||||||
|
|
||||||
### HashMap Core
|
|
||||||
- ✅ All CRUD operations
|
|
||||||
- ✅ Iterator implementations
|
|
||||||
- ✅ Resizing logic
|
|
||||||
- ✅ Collision handling
|
|
||||||
- ✅ Edge cases
|
|
||||||
|
|
||||||
### Hash Functions
|
|
||||||
- ✅ All primitive types
|
|
||||||
- ✅ All object types
|
|
||||||
- ✅ Special numeric values
|
|
||||||
- ✅ Error paths (circular references)
|
|
||||||
- ✅ Consistency guarantees
|
|
||||||
|
|
||||||
### Data Structures
|
|
||||||
- ✅ Node creation
|
|
||||||
- ✅ Chain linking
|
|
||||||
- ✅ Value storage
|
|
||||||
|
|
||||||
## Continuous Testing Strategy
|
|
||||||
|
|
||||||
### Pre-commit
|
|
||||||
```bash
|
|
||||||
bun test
|
|
||||||
```
|
|
||||||
|
|
||||||
### During Development
|
|
||||||
```bash
|
|
||||||
bun test --watch
|
|
||||||
```
|
|
||||||
|
|
||||||
### CI/CD Pipeline
|
|
||||||
```bash
|
|
||||||
bun test --coverage
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test Maintenance
|
|
||||||
|
|
||||||
### Adding New Tests
|
|
||||||
1. Create test in appropriate test file
|
|
||||||
2. Follow existing naming conventions
|
|
||||||
3. Ensure isolation from other tests
|
|
||||||
4. Verify coverage increases or maintains 100%
|
|
||||||
|
|
||||||
### Updating Tests
|
|
||||||
1. Update tests when API changes
|
|
||||||
2. Add tests for new edge cases
|
|
||||||
3. Refactor tests when code refactors
|
|
||||||
4. Keep test descriptions accurate
|
|
||||||
|
|
||||||
### Test Quality Checklist
|
|
||||||
- [ ] Test name clearly describes behavior
|
|
||||||
- [ ] Test is isolated and independent
|
|
||||||
- [ ] Edge cases are covered
|
|
||||||
- [ ] Assertions are specific and clear
|
|
||||||
- [ ] Test runs quickly (< 100ms typical)
|
|
||||||
- [ ] No console warnings or errors
|
|
||||||
|
|
||||||
## Common Test Patterns
|
|
||||||
|
|
||||||
### Testing Iterators
|
|
||||||
```typescript
|
|
||||||
const items = Array.from(map.entries());
|
|
||||||
expect(items).toHaveLength(3);
|
|
||||||
expect(items).toContainEqual(["key", "value"]);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Testing Error Conditions
|
|
||||||
```typescript
|
|
||||||
expect(() => new HashMap(0)).toThrow();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Testing Custom Implementations
|
|
||||||
```typescript
|
|
||||||
const customHash = new CustomHashFunction();
|
|
||||||
const map = new HashMap(16, 0.75, customHash);
|
|
||||||
// Test custom behavior
|
|
||||||
```
|
|
||||||
|
|
||||||
### Testing Large Datasets
|
|
||||||
```typescript
|
|
||||||
for (let i = 0; i < 1000; i++) {
|
|
||||||
map.set(`key${i}`, i);
|
|
||||||
}
|
|
||||||
expect(map.size).toBe(1000);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test Performance
|
|
||||||
|
|
||||||
Average test execution time: **12ms** for all 66 tests
|
|
||||||
|
|
||||||
Individual test timing:
|
|
||||||
- Simple operations: < 1ms
|
|
||||||
- Iterator tests: 3-5ms
|
|
||||||
- Large dataset tests: 60-80ms
|
|
||||||
- Circular reference tests: ~100ms (due to error handling)
|
|
||||||
|
|
||||||
## Future Testing Enhancements
|
|
||||||
|
|
||||||
### Potential Additions
|
|
||||||
1. **Property-Based Testing**: Use fast-check for random input testing
|
|
||||||
2. **Mutation Testing**: Verify test quality with Stryker
|
|
||||||
3. **Benchmark Tests**: Performance regression detection
|
|
||||||
4. **Memory Leak Tests**: Long-running operation validation
|
|
||||||
5. **Concurrent Access Tests**: Thread safety (if needed)
|
|
||||||
|
|
||||||
### Coverage Goals
|
|
||||||
- Maintain 100% line coverage
|
|
||||||
- Add branch coverage reporting
|
|
||||||
- Add mutation score tracking
|
|
||||||
- Monitor test execution time
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
This test suite provides comprehensive coverage of the HashMap implementation, achieving 100% line coverage with 66 well-designed tests. The tests verify:
|
|
||||||
|
|
||||||
- ✅ All SOLID principles are maintained
|
|
||||||
- ✅ All edge cases are handled correctly
|
|
||||||
- ✅ Performance characteristics are validated
|
|
||||||
- ✅ API contracts are enforced
|
|
||||||
- ✅ Error conditions are properly managed
|
|
||||||
|
|
||||||
The testing strategy ensures the HashMap implementation is robust, reliable, and maintainable.
|
|
||||||
|
|
||||||
15
eslint.config.ts
Normal file
15
eslint.config.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import js from "@eslint/js";
|
||||||
|
import globals from "globals";
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
import json from "@eslint/json";
|
||||||
|
import markdown from "@eslint/markdown";
|
||||||
|
import css from "@eslint/css";
|
||||||
|
import { defineConfig } from "eslint/config";
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
{ files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], plugins: { js }, extends: ["js/recommended"], languageOptions: { globals: globals.node } },
|
||||||
|
tseslint.configs.recommended,
|
||||||
|
{ files: ["**/*.json"], plugins: { json }, language: "json/json", extends: ["json/recommended"] },
|
||||||
|
{ files: ["**/*.md"], plugins: { markdown }, language: "markdown/commonmark", extends: ["markdown/recommended"] },
|
||||||
|
{ files: ["**/*.css"], plugins: { css }, language: "css/css", extends: ["css/recommended"] },
|
||||||
|
]);
|
||||||
29
package.json
29
package.json
@@ -7,10 +7,22 @@
|
|||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"types": "src/index.ts",
|
"types": "src/index.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"ci-install": "bun install",
|
||||||
|
"ci-test": "bun test",
|
||||||
|
"ci-build": "bash scripts/ci-build.sh",
|
||||||
|
"ci-deploy:ga": "bash scripts/ci-deploy.sh --beta false",
|
||||||
|
"ci-deploy:beta": "bash scripts/ci-deploy.sh --beta true",
|
||||||
|
"format": "bun run prettier --write ./src/**/*.ts",
|
||||||
|
"lint": "eslint",
|
||||||
|
"prelint:fix": "bun run format",
|
||||||
|
"lint:fix": "eslint --fix",
|
||||||
"test": "bun test",
|
"test": "bun test",
|
||||||
"test:watch": "bun test --watch",
|
"build:node:debug": "bun build ./src/index.ts --target=node --sourcemap=none --format=esm --sourcemap=inline --outdir=dist/node",
|
||||||
"example:basic": "bun run src/examples/basic-usage.ts",
|
"build:browser:debug": "bun build ./src/index.ts --target=browser --sourcemap=none --format=esm --sourcemap=inline --outdir=dist/browser",
|
||||||
"example:custom": "bun run src/examples/custom-hash-function.ts"
|
"build:node": "bun build ./src/index.ts --target=node --sourcemap=none --format=esm --splitting --minify --outdir=dist/node",
|
||||||
|
"build:browser": "bun build ./src/index.ts --target=browser --sourcemap=none --format=esm --splitting --minify --outdir=dist/browser",
|
||||||
|
"build:types": "bunx tsc -p tsconfig.d.json",
|
||||||
|
"build:prepare-package-json": "bash scripts/prepare-package-json.sh"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"hashmap",
|
"hashmap",
|
||||||
@@ -24,7 +36,16 @@
|
|||||||
"author": "Techniker.me",
|
"author": "Techniker.me",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest"
|
"@eslint/css": "0.14.1",
|
||||||
|
"@eslint/js": "9.39.1",
|
||||||
|
"@eslint/json": "0.14.0",
|
||||||
|
"@eslint/markdown": "7.5.1",
|
||||||
|
"@types/bun": "latest",
|
||||||
|
"eslint": "9.39.1",
|
||||||
|
"globals": "16.5.0",
|
||||||
|
"jiti": "2.6.1",
|
||||||
|
"prettier": "3.6.2",
|
||||||
|
"typescript-eslint": "8.47.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
|
|||||||
33
scripts/ci-build.sh
Executable file
33
scripts/ci-build.sh
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#! /usr/bin/env bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o nounset
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
distDirectory=${DIST_DIRECTORY:-"dist"}
|
||||||
|
|
||||||
|
if [[ ! -z "${distDirectory}" ]]; then
|
||||||
|
echo "Removing dist directory [${distDirectory}]"
|
||||||
|
|
||||||
|
rm -rf ${distDirectory}
|
||||||
|
fi
|
||||||
|
|
||||||
|
bun run lint
|
||||||
|
bun run build:node
|
||||||
|
bun run build:browser
|
||||||
|
bun run build:types
|
||||||
|
bun run build:prepare-package-json
|
||||||
|
|
||||||
|
echo "Copying [.npmrc] to [${distDirectory}]"
|
||||||
|
cp .npmrc ./${distDirectory}
|
||||||
|
|
||||||
|
echo "Copying [.nvmrc] to [${distDirectory}]"
|
||||||
|
cp .nvmrc ./${distDirectory}
|
||||||
|
|
||||||
|
echo "Copying [README.md] to [${distDirectory}]"
|
||||||
|
cp README ./${distDirectory}
|
||||||
|
|
||||||
|
ls ${distDirectory}
|
||||||
|
|
||||||
|
echo -e "\nci-build complete!"
|
||||||
|
exit 0
|
||||||
83
scripts/ci-deploy.sh
Executable file
83
scripts/ci-deploy.sh
Executable file
@@ -0,0 +1,83 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o nounset
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
|
||||||
|
registryUrl="http://localhost:4873"
|
||||||
|
# registryUrl="https://registry-node.techniker.me"
|
||||||
|
packageVersionToDeploy=""
|
||||||
|
isBeta="false"
|
||||||
|
|
||||||
|
while [[ "${#}" -gt 0 ]]; do
|
||||||
|
case "${1}" in
|
||||||
|
--beta)
|
||||||
|
isBeta="${2}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--version)
|
||||||
|
packageVersionToDeploy="${2}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option [${1}]"
|
||||||
|
exit "${LINENO}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
function cleanDirectory {
|
||||||
|
local directory="${1}"
|
||||||
|
|
||||||
|
if [ -d "${directory}" ]; then
|
||||||
|
echo "Deleting [${directory}]..."
|
||||||
|
|
||||||
|
rm -rf "${directory}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function removePackageJsonMember {
|
||||||
|
local packageJsonPath="dist/package.json"
|
||||||
|
local memberToRemove="${1}"
|
||||||
|
|
||||||
|
if [ -f "${packageJsonPath}" ]; then
|
||||||
|
echo "Removing [${memberToRemove}] from the dist/package.json"
|
||||||
|
|
||||||
|
jq "del(.${memberToRemove})" "${packageJsonPath}" >tmp.$$.json && mv tmp.$$.json "$packageJsonPath"
|
||||||
|
else
|
||||||
|
echo "Error: [${packageJsonPath}] not found."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePackageJsonVersion {
|
||||||
|
local versionToUpdate="${1}"
|
||||||
|
|
||||||
|
if [ isBeta == "true" ]; then
|
||||||
|
echo "Version to update [${versionToUpdate}] Contains beta"
|
||||||
|
echo "Updating package.json version to [${versionToUpdate}]"
|
||||||
|
|
||||||
|
local packageJsonVersion=$(jq -r '.version' package.json)
|
||||||
|
|
||||||
|
sed -i "s/\"version\": \"${packageJsonVersion}\"/\"version\": \"${versionToUpdate}\"/" dist/package.json
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Deploying [${packageVersionToDeploy}]"
|
||||||
|
echo "isBeta [${isBeta}]"
|
||||||
|
|
||||||
|
cleanDirectory "dist"
|
||||||
|
|
||||||
|
bun run ci-build
|
||||||
|
|
||||||
|
removePackageJsonMember "devDependencies"
|
||||||
|
removePackageJsonMember "scripts"
|
||||||
|
|
||||||
|
echo "publishing to ${registryUrl}"
|
||||||
|
if [ "${isBeta}" == "true" ]; then
|
||||||
|
updatePackageJsonVersion "${packageVersionToDeploy}"
|
||||||
|
npm publish --registry "${registryUrl}" --tag beta
|
||||||
|
else
|
||||||
|
npm publish --registry "${registryUrl}"
|
||||||
|
fi
|
||||||
|
|
||||||
16
scripts/prepare-package-json.sh
Executable file
16
scripts/prepare-package-json.sh
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o nounset
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
distDirectory=${DIST_DIRECTORY:-"dist"}
|
||||||
|
packageJsonPath=${PACKAGE_JSON_PATH:-"package.json"}
|
||||||
|
|
||||||
|
if [ ! -d "${distDirectory}" ]; then
|
||||||
|
echo "Unable to prepare package.json, [${distDirectory}] not found"
|
||||||
|
exit $LINENO
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Preparing [package.json] to [${distDirectory}]"
|
||||||
|
jq '{name, version, author, type, types, exports, files, publishConfig}' "${packageJsonPath}" > "${distDirectory}/package.json"
|
||||||
@@ -31,7 +31,7 @@ export class HashMap<K, V> implements IHashMap<K, V>, Iterable<[K, V]> {
|
|||||||
constructor(
|
constructor(
|
||||||
initialCapacity: number = 16,
|
initialCapacity: number = 16,
|
||||||
loadFactorThreshold: number = 0.75,
|
loadFactorThreshold: number = 0.75,
|
||||||
hashFunction?: IHashFunction<K>
|
hashFunction?: IHashFunction<K>,
|
||||||
) {
|
) {
|
||||||
if (initialCapacity <= 0) {
|
if (initialCapacity <= 0) {
|
||||||
throw new Error("Initial capacity must be positive");
|
throw new Error("Initial capacity must be positive");
|
||||||
@@ -276,4 +276,3 @@ export class HashMap<K, V> implements IHashMap<K, V>, Iterable<[K, V]> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,4 +85,3 @@ console.log(` Load factor: ${map.loadFactor.toFixed(2)}`);
|
|||||||
console.log("");
|
console.log("");
|
||||||
|
|
||||||
console.log("=== Examples Complete ===");
|
console.log("=== Examples Complete ===");
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ console.log("1. Using NumericHashFunction:");
|
|||||||
const numericMap = new HashMap<number, string>(
|
const numericMap = new HashMap<number, string>(
|
||||||
16,
|
16,
|
||||||
0.75,
|
0.75,
|
||||||
new NumericHashFunction()
|
new NumericHashFunction(),
|
||||||
);
|
);
|
||||||
|
|
||||||
numericMap.set(12345, "value1");
|
numericMap.set(12345, "value1");
|
||||||
@@ -45,7 +45,7 @@ class CaseInsensitiveHashFunction implements IHashFunction<string> {
|
|||||||
const caseInsensitiveMap = new HashMap<string, number>(
|
const caseInsensitiveMap = new HashMap<string, number>(
|
||||||
16,
|
16,
|
||||||
0.75,
|
0.75,
|
||||||
new CaseInsensitiveHashFunction()
|
new CaseInsensitiveHashFunction(),
|
||||||
);
|
);
|
||||||
|
|
||||||
caseInsensitiveMap.set("Hello", 1);
|
caseInsensitiveMap.set("Hello", 1);
|
||||||
@@ -96,7 +96,11 @@ class ModuloHashFunction implements IHashFunction<number> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const moduloMap = new HashMap<number, string>(8, 0.75, new ModuloHashFunction());
|
const moduloMap = new HashMap<number, string>(
|
||||||
|
8,
|
||||||
|
0.75,
|
||||||
|
new ModuloHashFunction(),
|
||||||
|
);
|
||||||
|
|
||||||
for (let i = 0; i < 20; i++) {
|
for (let i = 0; i < 20; i++) {
|
||||||
moduloMap.set(i, `value-${i}`);
|
moduloMap.set(i, `value-${i}`);
|
||||||
@@ -108,4 +112,3 @@ console.log(` Get 15: ${moduloMap.get(15)}`);
|
|||||||
console.log("");
|
console.log("");
|
||||||
|
|
||||||
console.log("=== Custom Hash Function Examples Complete ===");
|
console.log("=== Custom Hash Function Examples Complete ===");
|
||||||
|
|
||||||
|
|||||||
@@ -40,4 +40,3 @@ export class DefaultHashFunction<K> implements IHashFunction<K> {
|
|||||||
return String(key);
|
return String(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,4 +22,3 @@ export class NumericHashFunction implements IHashFunction<number> {
|
|||||||
return Math.floor(capacity * fractionalPart);
|
return Math.floor(capacity * fractionalPart);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,4 +12,3 @@ export interface IHashFunction<K> {
|
|||||||
*/
|
*/
|
||||||
hash(key: K, capacity: number): number;
|
hash(key: K, capacity: number): number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,4 +63,3 @@ export interface IHashMap<K, V> {
|
|||||||
*/
|
*/
|
||||||
forEach(callback: (value: V, key: K, map: IHashMap<K, V>) => void): void;
|
forEach(callback: (value: V, key: K, map: IHashMap<K, V>) => void): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ export class HashNode<K, V> {
|
|||||||
constructor(
|
constructor(
|
||||||
public key: K,
|
public key: K,
|
||||||
public value: V,
|
public value: V,
|
||||||
public next: HashNode<K, V> | null = null
|
public next: HashNode<K, V> | null = null,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user