Skip to content

Releases: radashi-org/radashi

v12.3.0

01 Dec 22:11
Compare
Choose a tag to compare

New Functions

Add isClass function → PR #239

The isClass function determines if a value was declared using ES6 class syntax, distinguishing modern class declarations from traditional constructor functions or other types.

  • Only returns true for values created with the class keyword
  • Old-style constructor functions will return false
  • Built-in native class constructors (like Error) return false
  • Works with type narrowing for TypeScript
import * as _ from 'radashi'

class MyClass {
  x = 1
}

function OldConstructor() {
  this.x = 1
}

// Examples
_.isClass(MyClass) // true
_.isClass(OldConstructor) // false
_.isClass('string') // false
_.isClass(Error) // false

Thanks to Marlon Passos and Alec Larson for their work on this feature!

🔗 Docs / Source / Tests

 

Add isNullish function → PR #277

The isNullish function is a type-checking utility that determines whether a given value is either null or undefined. It helps you avoid the typos that an x == null check is prone to, and it's shorter to write than x === undefined || x === null.

import * as _ from 'radashi'

// Basic usage examples
_.isNullish(null) // true
_.isNullish(undefined) // true
_.isNullish('') // false
_.isNullish([]) // false
_.isNullish(0) // false

Thanks to Wei Xiaopeng for their work on this feature!

🔗 Docs / Source / Tests

 

Add cartesianProduct function → PR #241

The cartesianProduct function generates all possible combinations of elements from multiple input arrays, creating a new array containing every unique permutation of elements from those arrays.

  • Works with any number of input arrays
  • Returns an array of arrays representing all combinations
  • Preserves the order of input arrays in the resulting combinations
  • Can handle arrays of different types
import * as _ from 'radashi'

const colors = ['red', 'blue']
const numbers = [1, 2, 3]
const booleans = [true, false]

// Generate all combinations of colors, numbers, and booleans
const combinations = _.cartesianProduct(colors, numbers, booleans)

Thanks to Yam Borodetsky, Marlon Passos, and Alec Larson for their work on this feature!

🔗 Docs / Source / Tests

 

Add isUndefined function → PR #305

The isUndefined function is a type guard that checks whether a given value is specifically undefined. It provides a simple and type-safe way to determine if a value has been left unassigned or is explicitly set to undefined.

  • Strictly checks for undefined using the typeof operator
  • Can be used for type narrowing in TypeScript
import * as _ from 'radashi'

// Basic usage examples
const result1 = _.isUndefined(undefined) // true
const result2 = _.isUndefined(null) // false
const result3 = _.isUndefined(42) // false

Thanks to RobinBobin for their work on this feature!

🔗 Docs / Source / Tests

 

Add timeout function → PR #250

The timeout function creates a promise that rejects after a specified delay, providing a way to set timeouts for asynchronous operations. It allows customizing the error message or type of error thrown when the timeout occurs.

  • Can be used with a default TimeoutError or a custom error message/function
  • Primarily useful with Promise.race to add timeout functionality to async tasks
import * as _ from 'radashi'

// Basic usage: reject after 1 second with default TimeoutError
const basicTimeout = _.timeout(1000)

// With custom message
const customMessageTimeout = _.timeout(1000, 'Operation took too long')

// With Promise.race to limit async task duration
const someAsyncTask = async () => {
  await _.sleep(5000) // Simulate a long-running task
  return 'Task completed'
}

// Will reject after 1 second if task doesn't complete
const racedTask = await Promise.race([
  someAsyncTask(),
  _.timeout(1000, 'Task exceeded time limit'),
])

Thanks to Marlon Passos and Alec Larson for their work on this feature!

🔗 Docs / Source / Tests

 

Add dedent function → PR #120

The dedent function removes indentation from a multi-line string, making it easy to format and clean up text templates while preserving the relative indentation of embedded content.

  • Supports both explicit and auto-detected indentation
  • Works with tagged template strings
  • Automatically handles multi-line embedded strings
  • Removes the first leading and first trailing empty line
  • Preserves relative indentation of content
import * as _ from 'radashi'

// Remove auto-detected indentation
const message = _.dedent`
  Hello, world!
  This is a dedented message.
`
// => 'Hello, world!\nThis is a dedented message.'

// Remove specific indentation
const customMessage = _.dedent('\n    Hello\n    World!\n\n', '  ')
// => '  Hello\n  World!\n'

Thanks to Alec Larson for their work on this feature!

🔗 Docs / Source / Tests

 

New Features

Add signal option to retry → PR #262

The new feature introduces an optional signal parameter to both the retry function, allowing for manual interruption of async operations using AbortController.

  • The signal option accepts an AbortController.signal
  • When the signal is aborted, no more calls to the callback will be made
  • Aborting will throw a DOMException with the message "This operation was aborted"
  • Works consistently across Node.js and browser environments
import * as _ from 'radashi'

const abortController = new AbortController()
const signal = abortController.signal

const promise = _.retry(
  { times: 3, delay: 1000, signal },
  async () => await fetchSomeData(),
)

// To abort the operation:
abortController.abort()

Thanks to ts-saidbe.abdiganiev and Alec Larson for their work on this feature!

 

Add signal option to parallel → PR #262

The latest update adds an optional signal option to the parallel function, allowing you to interrupt parallel processing with an AbortController.

  • The signal option works with an AbortController.signal
  • When aborted, it will throw a DOMException with an "AbortError" name
  • Interruption only stops future iterations, not in-progress async calls
  • Signals are compatible with both browser and Node.js environments
import * as _ from 'radashi'

// Abort a parallel operation
const abortController = new AbortController()
const signal = abortController.signal

const pizzas = await _.parallel(
  { limit: 2, signal },
  ['pepperoni', 'cheese', 'mushroom'],
  async topping => {
    return await bakePizzaInWoodFiredOven(topping)
  },
)

// Abort the operation if needed
abortController.abort()

Thanks to ts-saidbe.abdiganiev and Alec Larson for their work on this feature!

 

Tolerate out-of-range parallel limit → PR #238

The parallel function now automatically clamps the concurrency limit between 1 and the input array's length, ensuring more predictable and safe parallel processing.

  • Previously, passing a limit larger than the array length or less than 1 could cause unexpected behavior
  • The limit is now automatically adjusted to be within the valid range
import * as _ from 'radashi'

// Limit will be adjusted to 3 (array length)
const results = await _.parallel(
  10,
  ['item1', 'item2', 'item3'],
  async item => {
    // Process each item
    return item.toUpperCase()
  },
)

Thanks to Marlon Passos and [Alec Larson](https://...

Read more

v12.2.3

12 Nov 18:17
Compare
Choose a tag to compare

Fixed

v12.2.2

12 Nov 17:48
Compare
Choose a tag to compare

Types

v12.2.1

12 Nov 17:47
Compare
Choose a tag to compare

Types

New Contributors

v12.2.0 – Radashi's first stable release

01 Nov 21:25
Compare
Choose a tag to compare

Starting today, you can install [email protected] from NPM. This is the first stable release of Radashi, which we've been incubating for more than 4 months, starting on June 23.

Now, we look forward to merging more pull requests, attracting more users, and inspiring more talented folks to join us in building (and just as important, maintaining) the next-generation utility toolkit that is Radashi.

Lastly, I'd like to ask everyone to check out the proposed features open for discussion, and we hope you'll share with us any ideas you may have as well. Remember, this is a community effort. Your perspective is valuable and it will help us make Radashi better with your needs in mind.

New Functions

Add cloneDeep function → PR #81

The cloneDeep function creates a deep copy of an object or array.

  • It supports cloning of plain objects, arrays, Map instances, and Set instances by default.
  • The default behavior can be customized by providing a partial CloningStrategy implementation.
import * as _ from 'radashi'

const obj = { a: 1, b: { c: 2 } }
const clone = _.cloneDeep(obj)

// The clone and its nested objects have their own identity. Therefore,
// mutating them won't affect the original object, and vice versa.
assert(clone !== obj)
assert(clone.b !== obj.b)
assert(JSON.stringify(clone) === JSON.stringify(obj))

🔗 Docs / Source / Tests

Add once function → PR #80

Create a wrapper around a given function such that it executes at most once.

  • Subsequent calls to the wrapped function return the result from the first execution, regardless of the arguments provided. This behavior is akin to “memoization” but specifically designed for single-use functions.
  • Use _.once.reset(fn) to clear the stored result and allow the function to execute again.
import * as _ from 'radashi'

const fn = _.once(() => Math.random())
fn() // 0.5
fn() // 0.5

_.once.reset(fn)
fn() // 0.8

🔗 Docs / Source / Tests

Cast a non-nullish value into an array → PR #97

The castArrayIfExists function ensures that a non-nullish input value is always returned as an array.

  • If the input is already an array, it returns a shallow copy of the array.
  • If the input is not an array, it wraps the input in a new array.
  • Nullish values (null or undefined) are passed through as is.
import * as _ from 'radashi'

_.castArrayIfExists(1) // => [1]
_.castArrayIfExists([1, 2, 3]) // => [1, 2, 3]
_.castArrayIfExists('hello') // => ['hello']
_.castArrayIfExists(null) // => null
_.castArrayIfExists(undefined) // => undefined

🔗 Docs / Source / Tests

Cast a value into an array → PR #97

The castArray function ensures that the input value is always returned as an array.

  • If the input is already an array, it returns a shallow copy of the array.
  • If the input is not an array, it wraps the input in a new array.
import * as _ from 'radashi'

_.castArray(1) // => [1]
_.castArray([1, 2, 3]) // => [1, 2, 3]
_.castArray('hello') // => ['hello']

🔗 Docs / Source / Tests

Convert an array to a map → PR #58

The mapify function allows you to convert an array into a Map object, where the keys and values are determined by provided functions.

  • The first callback determines the keys of the map.
  • The second callback determines the values of the map. If not provided, the original array elements are used as values.
import * as _ from 'radashi'

const fish = [
  { name: 'Marlin', weight: 105 },
  { name: 'Bass', weight: 8 },
  { name: 'Trout', weight: 13 },
]

const fishByName = _.mapify(fish, f => f.name)
// => Map(3) {'Marlin' => { name: 'Marlin', weight: 105 }, 'Bass' => { name: 'Bass', weight: 8 }, 'Trout' => { name: 'Trout', weight: 13 }}

const fishWeightByName = _.mapify(
  fish,
  f => f.name,
  f => f.weight,
)
// => Map(3) { 'Marlin' => 105, 'Bass' => 8, 'Trout' => 13 }

🔗 Docs / Source / Tests

Round a number to a specified precision → PR #53

The round function allows you to round a number to a specified precision.

  • The precision can be a positive or negative integer.
  • An optional rounding function (e.g. Math.floor or Math.ceil) can be provided. The default rounding function is Math.round.
import * as _ from 'radashi'

_.round(123.456) // => 123
_.round(1234.56, -2) // => 1200
_.round(4.001, 2, Math.ceil) // => 4.01
_.round(4.089, 2, Math.floor) // => 4.08

🔗 Docs / Source / Tests

Allow deep traversal of objects and arrays → PR #59

The traverse function recursively visits each property of an object (or each element of an array) and its nested objects or arrays.

  • By default, the only nested objects to be traversed are plain objects and arrays.
  • Traversal is performed in a depth-first manner, and circular references are skipped.
  • The traversal can be customized with a TraverseOptions object.
import * as _ from 'radashi'

const root = { a: { b: 2 }, c: [1, 2] }

_.traverse(root, (value, key, parent, context) => {
  const indent = '  '.repeat(context.parents.length)
  console.log(`${indent}${key} => ${value}`)
})
// Logs the following:
//   a => { b: 2 }
//     b => 2
//   c => [1, 2]
//     0 => 1
//     1 => 2

🔗 Docs / Source / Tests

Calculate string similarity → PR #122

The similarity function calculates the Levenshtein distance between two input strings, which represents the minimum number of single-character edits (insertions, deletions, or substitutions) required to change one string into the other.

  • A lower number indicates higher similarity, with 0 meaning the strings are identical.
  • The comparison is both case-sensitive and whitespace-significant.
  • The argument order doesn't matter, as it's symmetric.
import * as _ from 'radashi'

// Identical strings
_.similarity('hello', 'hello') // => 0

// One character difference
_.similarity('kitten', 'sitten') // => 1

// Multiple differences
_.similarity('saturday', 'sunday') // => 3

🔗 Docs / Source / Tests

Cast a value into a comparator function → PR #34

The castComparator function allows you to create a comparator function that can be passed into Array.prototype.sort.

Parameters:

  • mapping is either a property name or a mapping function.
  • compare is an optional custom compare function (e.g. for localeCompare use cases).
  • reverse reverses the comparison order if set to true.
import * as _ from 'radashi'

const users = [
  { id: 1, firstName: 'Alice', lastName: 'Smith' },
  { id: 3, firstName: 'Charlie', lastName: 'Brown' },
  { id: 2, firstName: 'Drew', lastName: 'Johnson' },
]

const compareById = _.castComparator('id')
users.sort(compareById)
// [Alice, Drew, Charlie]

const compareByFullName = _.castComparator(
  user => `${user.firstName} ${user.lastName}`,
  (a, b) => b.localeCompare(a),
)
users.sort(compareByFullName)
// [Alice, Charlie, Drew]

const compareByFullNameReversed = _.castComparator(
  user => `${user.firstName} ${user.lastName}`,
  (a, b) => b.localeCompare(a),
  true,
)
users.sort(compareByFullNameReversed)
// [Drew, Charlie, Alice]

🔗 Docs / Source / Tests

Cast a value into a mappin...

Read more