js-utilities
Utility functions for all JavaScript/TypeScript environments.
Table of Contents
Installation
To install the package, use npm:
npm install @andranik-arakelyan/js-utilities
Usage
Import the utilities you need in your project:
import {arraySubtract} from '@andranik-arakelyan/js-utilities';
const result = arraySubtract([1, 3, 5] , [ 1, 2, 3]);
console.log( 'result', result ); // [5]
Features
Data Structures
Stack<T>
A generic type-safe implementation of a Stack data structure with LIFO (Last-In-First-Out) operations.
import { Stack } from '@andranik-arakelyan/js-utilities';
// Create a new stack of numbers
const stack = new Stack<number>();
// Push elements onto the stack
stack.push(1);
stack.push(2);
stack.push(3);
console.log(stack.peek()); // 3 (returns the top element without removing it)
console.log(stack.pop()); // 3 (removes and returns the top element)
console.log(stack.size()); // 2 (returns the current number of elements)
console.log(stack.isEmpty()); // false
Queue<T>
A generic type-safe implementation of a Queue data structure with FIFO (First-In-First-Out) operations.
import { Queue } from '@andranik-arakelyan/js-utilities';
// Create a new queue of strings
const queue = new Queue<string>();
// Enqueue elements
queue.enqueue("first");
queue.enqueue("second");
queue.enqueue("third");
console.log(queue.peek()); // "first" (returns the front element without removing it)
console.log(queue.dequeue()); // "first" (removes and returns the front element)
console.log(queue.size()); // 2 (returns the current number of elements)
console.log(queue.isEmpty()); // false
Array Utilities
arraySubtract
Subtracts elements of one array from another.
import { arraySubtract } from '@andranik-arakelyan/js-utilities';
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [1, 3, 5];
const result = arraySubtract(arr1, arr2);
console.log(result); // [2, 4]
arraySplit
Splits an array into chunks based on a separator.
import { arraySplit } from '@andranik-arakelyan/js-utilities';
// Using a value as separator
const result1 = arraySplit([1, 2, 3, 0, 4, 5, 0, 6], 0);
console.log(result1); // [[1, 2, 3], [4, 5], [6]]
// Using a function as separator
const result2 = arraySplit([1, 2, 3, 4, 5, 6], (item) => item % 2 === 0);
console.log(result2); // [[1], [3], [5]]
shuffle
Randomly shuffles an array using the Fisher-Yates algorithm. This algorithm is superior to naive shuffling approaches (like sorting with a random comparator) because it guarantees a truly random permutation with equal probability for each possible outcome and has optimal O(n) time complexity.
import { shuffle } from '@andranik-arakelyan/js-utilities';
const array = [1, 2, 3, 4, 5];
const shuffled = shuffle(array);
console.log(shuffled); // Example output: [3, 1, 5, 2, 4]
console.log(array); // Original array remains unchanged: [1, 2, 3, 4, 5]
unique
Returns a new array with duplicate values removed. Optionally accepts a function to determine the key for uniqueness comparison.
import { unique } from '@andranik-arakelyan/js-utilities';
// Remove duplicates from primitive values
const numbers = [1, 2, 2, 3, 1, 4];
console.log(unique(numbers)); // [1, 2, 3, 4]
// With objects using a custom key selector
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 1, name: 'Alice (duplicate)' }
];
const uniqueUsers = unique(users, user => user.id);
console.log(uniqueUsers); // [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
groupBy
Groups array elements by a key selector function. Returns a Map where keys are the result of the key selector function and values are arrays of elements that share the same key.
import { groupBy } from '@andranik-arakelyan/js-utilities';
// Group numbers by even/odd
const numbers = [1, 2, 3, 4, 5, 6];
const grouped = groupBy(numbers, n => n % 2 === 0 ? 'even' : 'odd');
console.log(grouped); // Map { 'odd' => [1, 3, 5], 'even' => [2, 4, 6] }
// Group objects by property
const users = [
{ name: 'Alice', department: 'Engineering' },
{ name: 'Bob', department: 'Marketing' },
{ name: 'Charlie', department: 'Engineering' }
];
const byDepartment = groupBy(users, user => user.department);
console.log(byDepartment);
// Map {
// 'Engineering' => [
// { name: 'Alice', department: 'Engineering' },
// { name: 'Charlie', department: 'Engineering' }
// ],
// 'Marketing' => [{ name: 'Bob', department: 'Marketing' }]
// }
// Group by multiple criteria using composite keys
const products = [
{ name: 'Laptop', category: 'Electronics', inStock: true },
{ name: 'Phone', category: 'Electronics', inStock: false },
{ name: 'Desk', category: 'Furniture', inStock: true }
];
const byStatus = groupBy(products, p => `${p.category}-${p.inStock}`);
// Groups by category and stock status combined
Object Utilities
deepClone
Creates a deep clone of an object or array, creating a new copy of all nested objects and arrays. This function handles circular references and various JavaScript built-in types.
import { deepClone } from '@andranik-arakelyan/js-utilities';
// Simple object cloning
const obj = { a: 1, b: { c: 2 } };
const clone = deepClone(obj);
clone.b.c = 3;
console.log(obj.b.c); // Still 2
// Handling arrays and nested structures
const arr = [1, { x: [2, 3] }];
const cloneArr = deepClone(arr);
cloneArr[1].x[0] = 10;
console.log(arr[1].x[0]); // Still 2
// Handling circular references
const circular = { a: 1 };
circular.self = circular;
const cloneCircular = deepClone(circular);
console.log(cloneCircular.self === cloneCircular); // true
// Handling built-in types
const data = {
date: new Date(),
regex: /test/g,
set: new Set([1, 2, 3]),
map: new Map([['key', 'value']])
};
const cloneData = deepClone(data);
console.log(cloneData.date instanceof Date); // true
console.log(cloneData.regex instanceof RegExp); // true
console.log(cloneData.set instanceof Set); // true
console.log(cloneData.map instanceof Map); // true
Random Utilities
randomInt
Generates a random integer in a specified range.
import { randomInt } from '@andranik-arakelyan/js-utilities';
// Random number between 1 and 10
const random = randomInt(10, 1);
console.log(random); // Example output: 7
randomBoolean
Generates a random boolean value.
import { randomBoolean } from '@andranik-arakelyan/js-utilities';
const random = randomBoolean();
console.log(random); // Either true or false
Runtime Utilities
currentCodeInfo
Returns information about the current code execution context.
import { currentCodeInfo } from '@andranik-arakelyan/js-utilities';
function exampleFunction() {
const info = currentCodeInfo();
console.log(info);
// Output:
// {
// className: "",
// methodName: "exampleFunction",
// filepath: "/path/to/your/file.js",
// filename: "file.js",
// lineNumber: 4,
// columnNumber: 15
// }
}
exampleFunction();
Function Utilities
once
Creates a function that ensures the original function is only ever called once, regardless of how many times the returned function is called. Subsequent calls to the wrapped function return the result of the first invocation.
import { once } from '@andranik-arakelyan/js-utilities';
// Create a function that will only execute once
const initializeApp = once(() => {
console.log('App initialized!');
return 'Initialization complete';
});
// First call - function executes
const result1 = initializeApp(); // Logs: 'App initialized!'
// Subsequent calls - function is not executed again
const result2 = initializeApp(); // No log output
// Both calls return the same result
console.log(result1 === result2); // true
// Works with parameters and preserves this context
const obj = {
data: 'example',
process: once(function(this: { data: string }, prefix: string) {
console.log(`Processing ${prefix} ${this.data}`);
return `${prefix}-${this.data}`;
})
};
// First call with parameters
obj.process('test'); // Logs: 'Processing test example', returns: 'test-example'
// Second call - function is not executed again even with different parameters
obj.process('another'); // No log output, still returns: 'test-example'
debounce
Creates a debounced function that delays invoking the provided function until after the specified wait time has elapsed since the last time it was invoked. The function will only execute once the wait period has elapsed with no new calls. This is particularly useful when dealing with events that fire rapidly, such as window resizing, scrolling, or keystrokes, when you want to execute the handler only after the user has stopped the action.
import { debounce } from '@andranik-arakelyan/js-utilities';
// Create a debounced version of a function
const handleInput = debounce((value) => {
// This will only execute after the user has stopped typing for 300ms
console.log('Processing input:', value);
performSearch(value);
}, 300);
// Attach to an input event
inputElement.addEventListener('input', e => handleInput(e.target.value));
// Multiple rapid calls reset the timer each time
handleInput('a'); // Starts the timer
handleInput('ab'); // Cancels previous timer, starts a new one
handleInput('abc'); // Cancels previous timer, starts a new one
// After 300ms of no calls, the function executes with 'abc'
// With proper TypeScript typing
const calculateLayout = debounce((width: number, height: number): string => {
// Expensive calculation that should not run on every resize event
return `${width}x${height}`;
}, 100);
// Cancel a pending debounced call if needed
handleInput.cancel();
throttle
Creates a throttled function that only invokes the provided function at most once per every specified wait milliseconds, regardless of how many times it's called. This is useful when you want to limit the rate at which a function is executed, such as during scroll events or continuous button clicks.
import { throttle } from '@andranik-arakelyan/js-utilities';
// Create a throttled version of a function
const handleScroll = throttle(() => {
// This will execute at most once every 300ms, even if scrolling continuously
console.log('Processing scroll event');
}, 300);
// Attach to a scroll event
window.addEventListener('scroll', handleScroll);
// With proper TypeScript typing
const updatePosition = throttle((x: number, y: number): string => {
// Update UI based on position, but not too frequently
return `${x},${y}`;
}, 100);
// Cancel a pending throttled call if needed
handleScroll.cancel();
Promise Utilities
wait
Creates a promise that resolves after a specified delay.
import { wait } from '@andranik-arakelyan/js-utilities';
// Wait for 1 second
await wait(1000);
// Chain with other operations
wait(500).then(() => console.log('Half a second has passed'));
// Use in an async function
async function delayedOperation() {
console.log('Starting');
await wait(2000);
console.log('2 seconds have passed');
}
retry
Retries an async function with configurable attempts and exponential backoff.
import { retry } from '@andranik-arakelyan/js-utilities';
// Basic usage with default options (3 attempts)
const data = await retry(() => fetchData());
// With custom retry configuration
const result = await retry(
() => riskyOperation(),
{
attempts: 5, // Maximum attempts including initial attempt
delay: 1000, // Initial delay in milliseconds
backoffFactor: 2, // Multiply delay by this factor after each attempt
retryIf: (err) => err instanceof NetworkError, // Only retry specific errors
onRetry: (err, attempt) => console.log(`Retry attempt ${attempt}`) // Track retries
}
);
Contributing
Contributions are welcome! Please open an issue or submit a pull request.
Links
License
This project is licensed under the MIT License - see the LICENSE file for details.