Introduction
Welcome to this tutorial on creating a small utility library! In software development, utility libraries provide common, reusable functions that solve everyday programming tasks. Think of them as your personal toolkit - a collection of specialized tools ready to help you tackle different challenges efficiently.
In this tutorial, we'll use George Polya's famous 4-step problem-solving method to design and build our utility library:
- Understand the Problem - What exactly are we trying to build?
- Devise a Plan - How will we approach building this library?
- Execute the Plan - Writing the actual code
- Look Back and Reflect - Evaluating our solution and considering improvements
By the end of this tutorial, you'll have created a useful utility library you can use in future projects. Let's get started!
File Information
We'll be creating the following files:
utils/- Directory to contain our utility libraryutils/stringutils.js- String manipulation utilitiesutils/numberutils.js- Number manipulation utilitiesutils/arrayutils.js- Array manipulation utilitiesutils/validation.js- Input validation utilitiesutils/index.js- Main entry point that combines all utilitiesexamples.js- Example usage of our utility library
Step 1: Understand the Problem
Our assignment is to "Create a small utility library with multiple functions." Let's break this down:
What is a utility library?
A utility library is a collection of functions that perform common, reusable operations. These functions are typically:
- Focused on a single task
- Pure functions (produce the same output for the same input without side effects)
- Well-documented and easy to use
- Grouped by related functionality
What types of functions should our library include?
Let's plan to include utilities for common programming tasks such as:
- String manipulation (capitalization, formatting, validation)
- Number operations (rounding, formatting, validation)
- Array operations (filtering, sorting, transformation)
- Basic validation functions
Requirements Analysis
- The library should be modular (organized into logical categories)
- Functions should be pure when possible (same input produces same output)
- Functions should include proper error handling
- Each function should have clear documentation
- The library should be easy to import and use
Real-World Example
Popular utility libraries in JavaScript include Lodash and Underscore.js. These libraries provide hundreds of helper functions that make common tasks easier. Our library will be much smaller but follows the same principle - creating reusable functions that save time and reduce bugs.
Step 2: Devise a Plan
Whiteboard Plan
- Set up the directory structure for our utility library
- Define the categories of utility functions we'll create
- Design the API for each function (parameters, return values)
- Implement each utility function with error handling
- Create a main entry point to export all utilities
- Write examples to demonstrate each function
- Test the functions with various inputs
Function Categories and Specifications
String Utilities (stringutils.js)
capitalizeFirstLetter(str)- Capitalizes the first letter of a stringtruncate(str, length, ending)- Truncates a string to specified lengthslugify(str)- Converts a string to a URL-friendly slugreverseString(str)- Reverses a string
Number Utilities (numberutils.js)
formatCurrency(num, currency, locale)- Formats a number as currencyroundToDecimalPlaces(num, places)- Rounds a number to specified decimal placesrandomInRange(min, max)- Generates a random number within a rangeisEven(num)- Checks if a number is even
Array Utilities (arrayutils.js)
shuffle(array)- Randomly shuffles array elementschunk(array, size)- Splits an array into chunks of specified sizeunique(array)- Returns array with duplicate elements removedflatten(array)- Flattens a nested array structure
Validation Utilities (validation.js)
isValidEmail(email)- Validates email addressesisValidURL(url)- Validates URLsisValidPhoneNumber(phone, format)- Validates phone numbersisStrongPassword(password)- Checks if a password meets strength criteria
Organizing Functions Analogy
Think of our utility library like a well-organized kitchen. Each module (file) is like a drawer dedicated to a specific type of tool:
- String utilities = Cutting tools drawer (knives, scissors, etc.)
- Number utilities = Measuring tools drawer (measuring cups, scales, etc.)
- Array utilities = Organizing containers (tupperware, bins, etc.)
- Validation utilities = Safety equipment (oven mitts, fire extinguisher, etc.)
Each function is a specific tool in that drawer. Just as you wouldn't put a knife in the measuring drawer, we keep related functions together to make them easy to find when needed.
Step 3: Execute the Plan
Now let's start building our utility library, one module at a time.
Setting Up Directory Structure
First, create the necessary directories and files:
mkdir utils
touch utils/stringutils.js
touch utils/numberutils.js
touch utils/arrayutils.js
touch utils/validation.js
touch utils/index.js
touch examples.js
String Utilities Implementation
Let's implement our string utility functions:
File: utils/stringutils.js
/**
* String utility functions for common string operations
*/
/**
* Capitalizes the first letter of a string
* @param {string} str - The input string
* @returns {string} The string with first letter capitalized
* @throws {TypeError} If input is not a string
*/
function capitalizeFirstLetter(str) {
// Validate input is a string
if (typeof str !== 'string') {
throw new TypeError('Input must be a string');
}
// Handle empty string case
if (str.length === 0) {
return str;
}
// Capitalize first letter and concat with rest of string
return str.charAt(0).toUpperCase() + str.slice(1);
}
/**
* Truncates a string to specified length with optional ending
* @param {string} str - The input string
* @param {number} length - Maximum length of the result
* @param {string} [ending='...'] - String to append at the end
* @returns {string} Truncated string
* @throws {TypeError} If inputs are invalid types
*/
function truncate(str, length, ending = '...') {
// Input validation
if (typeof str !== 'string') {
throw new TypeError('First argument must be a string');
}
if (typeof length !== 'number' || length < 0) {
throw new TypeError('Length must be a non-negative number');
}
if (typeof ending !== 'string') {
throw new TypeError('Ending must be a string');
}
// Return original string if it's shorter than the specified length
if (str.length <= length) {
return str;
}
// Calculate truncation point accounting for ending length
const truncateLength = length - ending.length;
// If truncateLength is negative or zero, we can't fit the ending
if (truncateLength <= 0) {
return str.slice(0, length);
}
// Truncate and add ending
return str.slice(0, truncateLength) + ending;
}
/**
* Converts a string to a URL-friendly slug
* @param {string} str - The input string
* @returns {string} URL-friendly slug
* @throws {TypeError} If input is not a string
*/
function slugify(str) {
if (typeof str !== 'string') {
throw new TypeError('Input must be a string');
}
return str
.toLowerCase()
.trim()
.replace(/\s+/g, '-') // Replace spaces with hyphens
.replace(/[^\w\-]+/g, '') // Remove non-word chars except hyphens
.replace(/\-\-+/g, '-') // Replace multiple hyphens with single hyphen
.replace(/^-+/, '') // Remove leading hyphens
.replace(/-+$/, ''); // Remove trailing hyphens
}
/**
* Reverses a string
* @param {string} str - The input string
* @returns {string} The reversed string
* @throws {TypeError} If input is not a string
*/
function reverseString(str) {
if (typeof str !== 'string') {
throw new TypeError('Input must be a string');
}
// Converting to array, reversing, and joining is the clearest approach
return str.split('').reverse().join('');
// Alternative approach using array spread and reduce
// return [...str].reduce((rev, char) => char + rev, '');
}
// Export all functions
module.exports = {
capitalizeFirstLetter,
truncate,
slugify,
reverseString
};
Code Explanation
Let's break down a couple of these functions to understand them better:
capitalizeFirstLetter
This function takes a string and makes its first character uppercase. It uses two string methods:
charAt(0)- Gets the first character of the stringslice(1)- Gets everything after the first character
We then apply toUpperCase() to the first character and concatenate it with the rest of the string.
slugify
This function creates a URL-friendly version of a string (a "slug") by:
- Converting to lowercase
- Removing extra spaces
- Replacing spaces with hyphens
- Removing special characters
- Cleaning up any extra hyphens
We use regular expressions to handle these string transformations efficiently.
Number Utilities Implementation
Now let's implement our number utility functions:
File: utils/numberutils.js
/**
* Number utility functions for common number operations
*/
/**
* Formats a number as currency
* @param {number} num - The number to format
* @param {string} [currency='USD'] - Currency code
* @param {string} [locale='en-US'] - Locale code
* @returns {string} Formatted currency string
* @throws {TypeError} If num is not a number
*/
function formatCurrency(num, currency = 'USD', locale = 'en-US') {
// Input validation
if (typeof num !== 'number' || isNaN(num)) {
throw new TypeError('First argument must be a valid number');
}
// Use Intl.NumberFormat for localized currency formatting
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency
}).format(num);
}
/**
* Rounds a number to specified decimal places
* @param {number} num - The number to round
* @param {number} [places=0] - Number of decimal places
* @returns {number} Rounded number
* @throws {TypeError} If inputs are invalid
*/
function roundToDecimalPlaces(num, places = 0) {
// Input validation
if (typeof num !== 'number' || isNaN(num)) {
throw new TypeError('First argument must be a valid number');
}
if (!Number.isInteger(places) || places < 0) {
throw new TypeError('Decimal places must be a non-negative integer');
}
// Calculate multiplier based on decimal places
const multiplier = Math.pow(10, places);
// Round to specified decimal places
return Math.round(num * multiplier) / multiplier;
}
/**
* Generates a random number within a range (inclusive)
* @param {number} min - Minimum value (inclusive)
* @param {number} max - Maximum value (inclusive)
* @returns {number} Random number within range
* @throws {TypeError} If inputs are not numbers
* @throws {Error} If min is greater than max
*/
function randomInRange(min, max) {
// Input validation
if (typeof min !== 'number' || typeof max !== 'number' || isNaN(min) || isNaN(max)) {
throw new TypeError('Both arguments must be valid numbers');
}
if (min > max) {
throw new Error('Minimum value must be less than or equal to maximum value');
}
// Formula: Math.random() * (max - min + 1) + min
// Adding 1 to make max inclusive
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* Checks if a number is even
* @param {number} num - The number to check
* @returns {boolean} True if even, false otherwise
* @throws {TypeError} If input is not a number
*/
function isEven(num) {
// Input validation
if (typeof num !== 'number' || isNaN(num)) {
throw new TypeError('Input must be a valid number');
}
// Check if number is even using modulo operator
return num % 2 === 0;
}
// Export all functions
module.exports = {
formatCurrency,
roundToDecimalPlaces,
randomInRange,
isEven
};
Code Explanation
formatCurrency
This function utilizes the browser's built-in internationalization API (Intl.NumberFormat) to format numbers as currency strings with proper symbols and formatting conventions for different locales.
For example, calling formatCurrency(1234.56) would produce "$1,234.56" in the US, but "€1.234,56" in Germany if the locale was changed.
randomInRange
This function uses JavaScript's Math.random() to generate a random number within a specified inclusive range. The formula works by:
- Generating a random decimal between 0 (inclusive) and 1 (exclusive)
- Multiplying by the range size (plus 1 to include the max value)
- Adding the minimum value to shift the range
- Using Math.floor to convert to an integer
This gives us a random integer between min and max, inclusive.
Array Utilities Implementation
Let's implement our array utility functions:
File: utils/arrayutils.js
/**
* Array utility functions for common array operations
*/
/**
* Randomly shuffles array elements (Fisher-Yates algorithm)
* @param {Array} array - The array to shuffle
* @returns {Array} New shuffled array
* @throws {TypeError} If input is not an array
*/
function shuffle(array) {
// Input validation
if (!Array.isArray(array)) {
throw new TypeError('Input must be an array');
}
// Create a copy of the array to avoid modifying the original
const result = [...array];
// Fisher-Yates shuffle algorithm
for (let i = result.length - 1; i > 0; i--) {
// Generate random index from 0 to i
const j = Math.floor(Math.random() * (i + 1));
// Swap elements at i and j
[result[i], result[j]] = [result[j], result[i]];
}
return result;
}
/**
* Splits an array into chunks of specified size
* @param {Array} array - The array to chunk
* @param {number} size - Size of each chunk
* @returns {Array} Array of chunks
* @throws {TypeError} If inputs are invalid
*/
function chunk(array, size) {
// Input validation
if (!Array.isArray(array)) {
throw new TypeError('First argument must be an array');
}
if (!Number.isInteger(size) || size <= 0) {
throw new TypeError('Chunk size must be a positive integer');
}
// Initialize result array
const result = [];
// Create chunks using slice
for (let i = 0; i < array.length; i += size) {
result.push(array.slice(i, i + size));
}
return result;
}
/**
* Returns array with duplicate elements removed
* @param {Array} array - The input array
* @returns {Array} Array with unique elements
* @throws {TypeError} If input is not an array
*/
function unique(array) {
// Input validation
if (!Array.isArray(array)) {
throw new TypeError('Input must be an array');
}
// Use Set to remove duplicates and convert back to array
return [...new Set(array)];
// Alternative implementation using filter
// return array.filter((item, index) => array.indexOf(item) === index);
}
/**
* Flattens a nested array structure (one level deep)
* @param {Array} array - The array to flatten
* @returns {Array} Flattened array
* @throws {TypeError} If input is not an array
*/
function flatten(array) {
// Input validation
if (!Array.isArray(array)) {
throw new TypeError('Input must be an array');
}
// Simple one-level flattening using concat and spread
return [].concat(...array);
// Modern alternative using flat
// return array.flat();
}
// Export all functions
module.exports = {
shuffle,
chunk,
unique,
flatten
};
Code Explanation
shuffle
This function implements the Fisher-Yates (or Knuth) shuffle algorithm, which is an unbiased way to randomly reorder array elements. The algorithm works by:
- Starting from the last element
- Swapping it with a randomly selected element from the entire array (including itself)
- Moving to the second-to-last element and repeating
- Continuing until we reach the first element
We create a copy of the original array to avoid modifying it, following the principle of pure functions.
unique
This function removes duplicate elements from an array using JavaScript's Set object, which automatically eliminates duplicates. We then convert the Set back to an array using the spread operator.
An alternative approach using filter is included as a comment, which works by keeping only the first occurrence of each value based on its index.
Validation Utilities Implementation
Now let's implement our validation utility functions:
File: utils/validation.js
/**
* Validation utility functions for common input validation
*/
/**
* Validates if a string is a valid email address
* @param {string} email - The email address to validate
* @returns {boolean} True if valid, false otherwise
*/
function isValidEmail(email) {
if (typeof email !== 'string') {
return false;
}
// Email regex pattern
// This is a simple pattern and doesn't cover all edge cases
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailPattern.test(email);
}
/**
* Validates if a string is a valid URL
* @param {string} url - The URL to validate
* @returns {boolean} True if valid, false otherwise
*/
function isValidURL(url) {
if (typeof url !== 'string') {
return false;
}
try {
// Using URL constructor for validation
new URL(url);
return true;
} catch (error) {
return false;
}
}
/**
* Validates if a string is a valid phone number
* @param {string} phone - The phone number to validate
* @param {string} [format='any'] - Expected format ('any', 'us', 'intl')
* @returns {boolean} True if valid, false otherwise
*/
function isValidPhoneNumber(phone, format = 'any') {
if (typeof phone !== 'string') {
return false;
}
// Remove common formatting characters for consistency
const cleanPhone = phone.replace(/[\s\-\(\)\.]/g, '');
// Different validation patterns based on format
switch (format.toLowerCase()) {
case 'us':
// US format: 10 digits
return /^\d{10}$/.test(cleanPhone);
case 'intl':
// International format: + followed by 7-15 digits
return /^\+\d{7,15}$/.test(cleanPhone);
case 'any':
default:
// Any format: 7-15 digits, optionally with + prefix
return /^(\+)?\d{7,15}$/.test(cleanPhone);
}
}
/**
* Checks if a password meets strength criteria
* @param {string} password - The password to check
* @returns {boolean} True if strong, false otherwise
*/
function isStrongPassword(password) {
if (typeof password !== 'string') {
return false;
}
// Password must be at least 8 characters and contain:
// - At least one uppercase letter
// - At least one lowercase letter
// - At least one number
// - At least one special character
const hasMinLength = password.length >= 8;
const hasUppercase = /[A-Z]/.test(password);
const hasLowercase = /[a-z]/.test(password);
const hasNumber = /\d/.test(password);
const hasSpecialChar = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password);
return hasMinLength && hasUppercase && hasLowercase && hasNumber && hasSpecialChar;
}
// Export all functions
module.exports = {
isValidEmail,
isValidURL,
isValidPhoneNumber,
isStrongPassword
};
Code Explanation
isValidEmail
This function uses a regular expression to validate email addresses. The pattern checks for:
- At least one character before the @ symbol
- The @ symbol
- At least one character for the domain name
- A dot
- At least one character for the TLD (top-level domain)
Note that this is a simplified check. Complete email validation according to all RFCs is extremely complex.
isValidURL
This function leverages JavaScript's built-in URL constructor, which throws an error if the URL is invalid. Using a try/catch block, we can determine validity based on whether an error occurs.
This approach is more robust than using regex alone, as the URL constructor follows web standards for URL validation.
Creating the Main Entry Point
Now let's create the main entry point that combines all our utility modules:
File: utils/index.js
/**
* Main entry point for utility library
* Combines all utility modules into a single export
*/
// Import individual modules
const stringUtils = require('./stringutils');
const numberUtils = require('./numberutils');
const arrayUtils = require('./arrayutils');
const validation = require('./validation');
// Combine all utilities into a single object
const utils = {
string: stringUtils,
number: numberUtils,
array: arrayUtils,
validation: validation
};
// Export the combined utility object
module.exports = utils;
Code Explanation
This index.js file serves as the entry point for our utility library. It:
- Imports each utility module we've created
- Organizes them into a structured object with logical categories
- Exports the combined object for easy consumption
This approach allows users to access utilities through a clear, organized namespace structure like utils.string.capitalizeFirstLetter() or utils.array.unique().
Writing Examples
Let's create an examples file to demonstrate how to use our utility library:
File: examples.js
/**
* Examples demonstrating the use of our utility library
*/
// Import the utility library
const utils = require('./utils');
// String Utilities Examples
console.log('\n--- String Utilities ---');
console.log("capitalizeFirstLetter('hello world'):");
console.log(utils.string.capitalizeFirstLetter('hello world'));
// Output: "Hello world"
console.log("\ntruncate('This is a long sentence that needs truncating', 20):");
console.log(utils.string.truncate('This is a long sentence that needs truncating', 20));
// Output: "This is a long sen..."
console.log("\nslugify('Hello World! This is a title'):");
console.log(utils.string.slugify('Hello World! This is a title'));
// Output: "hello-world-this-is-a-title"
console.log("\nreverseString('JavaScript'):");
console.log(utils.string.reverseString('JavaScript'));
// Output: "tpircSavaJ"
// Number Utilities Examples
console.log('\n--- Number Utilities ---');
console.log("formatCurrency(1234.56):");
console.log(utils.number.formatCurrency(1234.56));
// Output: "$1,234.56"
console.log("\nformatCurrency(1234.56, 'EUR', 'de-DE'):");
console.log(utils.number.formatCurrency(1234.56, 'EUR', 'de-DE'));
// Output: "1.234,56 €"
console.log("\nroundToDecimalPlaces(3.14159, 2):");
console.log(utils.number.roundToDecimalPlaces(3.14159, 2));
// Output: 3.14
console.log("\nrandomInRange(1, 10) - run 5 times:");
for (let i = 0; i < 5; i++) {
console.log(utils.number.randomInRange(1, 10));
}
// Output: Random numbers between 1 and 10
console.log("\nisEven(42):");
console.log(utils.number.isEven(42));
// Output: true
// Array Utilities Examples
console.log('\n--- Array Utilities ---');
const sampleArray = [1, 2, 3, 4, 5];
console.log("shuffle([1, 2, 3, 4, 5]):");
console.log(utils.array.shuffle(sampleArray));
// Output: Randomly shuffled array
console.log("\nchunk([1, 2, 3, 4, 5, 6, 7, 8], 3):");
console.log(utils.array.chunk([1, 2, 3, 4, 5, 6, 7, 8], 3));
// Output: [[1, 2, 3], [4, 5, 6], [7, 8]]
console.log("\nunique([1, 2, 2, 3, 4, 4, 5]):");
console.log(utils.array.unique([1, 2, 2, 3, 4, 4, 5]));
// Output: [1, 2, 3, 4, 5]
console.log("\nflatten([[1, 2], [3, 4], [5]]):");
console.log(utils.array.flatten([[1, 2], [3, 4], [5]]));
// Output: [1, 2, 3, 4, 5]
// Validation Utilities Examples
console.log('\n--- Validation Utilities ---');
console.log("isValidEmail('user@example.com'):");
console.log(utils.validation.isValidEmail('user@example.com'));
// Output: true
console.log("\nisValidEmail('invalid-email'):");
console.log(utils.validation.isValidEmail('invalid-email'));
// Output: false
console.log("\nisValidURL('https://www.example.com'):");
console.log(utils.validation.isValidURL('https://www.example.com'));
// Output: true
console.log("\nisValidURL('not-a-url'):");
console.log(utils.validation.isValidURL('not-a-url'));
// Output: false
console.log("\nisValidPhoneNumber('(123) 456-7890', 'us'):");
console.log(utils.validation.isValidPhoneNumber('(123) 456-7890', 'us'));
// Output: true
console.log("\nisStrongPassword('Weak123'):");
console.log(utils.validation.isStrongPassword('Weak123'));
// Output: false
console.log("\nisStrongPassword('Strong123!'):");
console.log(utils.validation.isStrongPassword('Strong123!'));
// Output: true
Code Explanation
This examples.js file demonstrates how to use each function in our utility library. It's organized by module, showing:
- How to import and access our utility functions
- Example usage for each function with sample inputs
- Expected outputs as comments
Running this file will execute all examples and print the results to the console, helping users understand how each utility works.
Step 4: Look Back and Reflect
Evaluating Our Solution
Let's review what we've accomplished and identify potential improvements.
Strengths of Our Implementation
- Modular organization: Functions are grouped logically by category
- Good documentation: Each function has clear JSDoc comments explaining usage
- Thorough error handling: We validate inputs and throw appropriate errors
- Pure functions: Most functions don't modify inputs or have side effects
- Simple API: Functions are easy to understand and use
Areas for Improvement
- Test coverage: Adding unit tests would ensure reliability
- Browser compatibility: Some functions might not work in older browsers
- Performance optimization: Some functions could be optimized for very large inputs
- Additional functionality: Many more useful utilities could be added
- TypeScript definitions: Adding type definitions would improve developer experience
Alternative Implementations
Let's look at some alternative ways we could have implemented certain functions:
Reverse String
// Original implementation
function reverseString(str) {
return str.split('').reverse().join('');
}
// Alternative using Array.from and reduce
function reverseStringAlt1(str) {
return Array.from(str).reduce((rev, char) => char + rev, '');
}
// Alternative using a for loop (more performant for very long strings)
function reverseStringAlt2(str) {
let reversed = '';
for (let i = str.length - 1; i >= 0; i--) {
reversed += str[i];
}
return reversed;
}
Unique Array
// Original implementation
function unique(array) {
return [...new Set(array)];
}
// Alternative using filter and indexOf
function uniqueAlt1(array) {
return array.filter((item, index) => array.indexOf(item) === index);
}
// Alternative using reduce (more verbose but illustrative)
function uniqueAlt2(array) {
return array.reduce((result, item) => {
if (!result.includes(item)) {
result.push(item);
}
return result;
}, []);
}
Currency Formatting
// Original implementation (using Intl API)
function formatCurrency(num, currency = 'USD', locale = 'en-US') {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency
}).format(num);
}
// Alternative without Intl API (limited functionality)
function formatCurrencyAlt(num, symbol = '$') {
// Format number with two decimal places
const formattedNum = num.toFixed(2);
// Add commas for thousands
const parts = formattedNum.split('.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
// Add symbol and join parts
return symbol + parts.join('.');
}
Real-World Applications
Our utility library would be useful in many real-world scenarios:
Web Development
- String utilities for formatting user input
- Validation utilities for form validation
- Array utilities for processing API responses
- Number utilities for displaying prices and measurements
Content Management Systems
- Using slugify to create URL-friendly page titles
- Truncating excerpts of long content
- Validating user emails and URLs
- Formatting currency values for e-commerce
Data Processing Applications
- Chunking large datasets for batch processing
- Removing duplicate records with unique
- Formatting numeric values for reports
- Randomizing data samples with shuffle
Learning Outcomes
Through this project, we've learned several important concepts:
- Modular code organization: Grouping related functionality into separate modules
- Pure function design: Creating functions that don't modify inputs
- Defensive programming: Validating inputs and handling errors gracefully
- Documentation practices: Writing clear JSDoc comments
- API design: Creating intuitive, easy-to-use function interfaces
Running and Testing the Library
How to Run the Examples
To run the examples and see the utility library in action:
- Save all files with the structure and content described above
- Open a terminal in the project directory
- Run
node examples.js - You should see the results of all example function calls in the console
Incorporating Into Your Own Projects
To use this utility library in your own projects:
- Copy the
utilsdirectory into your project - Import the library in your JavaScript files:
const utils = require('./utils'); - Use the utilities as needed:
const slug = utils.string.slugify('My Article Title'); const formattedPrice = utils.number.formatCurrency(29.99); const uniqueIds = utils.array.unique(userIds); const isValid = utils.validation.isValidEmail(userEmail);
Extending the Library
You can easily extend this library with your own utility functions:
- Add new functions to existing modules
- Create new utility modules for different categories
- Update the index.js file to include your new modules
Example: Adding a dateutils.js module:
// utils/dateutils.js
function formatDate(date, format = 'MM/DD/YYYY') {
// Implementation...
}
function isWeekend(date) {
// Implementation...
}
module.exports = {
formatDate,
isWeekend
};
// Then update utils/index.js
const dateUtils = require('./dateutils');
const utils = {
// Existing modules...
date: dateUtils
};
Conclusion
In this tutorial, we've created a comprehensive utility library by following George Polya's 4-step problem-solving method:
- Understanding the problem: We defined what a utility library is and what types of functions it should include
- Devising a plan: We organized our functions into logical categories and designed their APIs
- Executing the plan: We implemented each utility function with proper documentation and error handling
- Looking back: We evaluated our solution, considered alternatives, and discussed real-world applications
The result is a modular, well-documented library of utility functions that can be used in various projects. While our library is relatively small, it demonstrates important principles of good software design:
- Logical organization
- Clear documentation
- Input validation and error handling
- Pure functions
- Consistent API design
You can use this library as a starting point for your own projects, extending it with additional utilities as needed. As you continue your programming journey, creating reusable libraries like this one will help you write more efficient, maintainable code.