Starting today, you can install radashi@12.2.0 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,
Mapinstances, andSetinstances by default. - The default behavior can be customized by providing a partial
CloningStrategyimplementation.
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))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.8Cast 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) // => undefinedCast 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']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 }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.floororMath.ceil) can be provided. The default rounding function isMath.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.08Allow 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
TraverseOptionsobject.
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 => 2Calculate 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') // => 3Cast 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:
mappingis either a property name or a mapping function.compareis an optional custom compare function (e.g. forlocaleCompareuse cases).reversereverses the comparison order if set totrue.
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]Cast a value into a mapping function → PR #43
Improve your own utility function by adding a flexible value-mapping option, using castMapping to retrieve a mapping function.
The following types can be casted into a mapping function:
- Function: If the input is a function, it returns the function as is.
- Property Name: If the input is a property name, it returns a function that retrieves the value of that property from an object.
- Nullish: If the input is nullish (null or undefined), it returns a function that simply returns the input object itself.
import * as _ from 'radashi'
// Using a property name
const getName = _.castMapping('name')
getName({ name: 'Alice' }) // => 'Alice'
// Using a function
const getLength = _.castMapping((str: string) => str.length)
getLength('Hello') // => 5
// Using undefined
const identity = _.castMapping(undefined)
identity({ any: 'value' }) // => { any: 'value' }Limit the range of a variable number → PR #106
The clamp function restricts a number to be within a specified range.
Parameters:
valueis the number to clamp.minis the minimum value (inclusive).maxis the maximum value (inclusive).
import * as _ from 'radashi'
_.clamp(5, 1, 10) // returns 5
_.clamp(0, 1, 10) // returns 1
_.clamp(15, 1, 10) // returns 10
// Invalid range
_.clamp(1, 10, 1) // throwsAdd unzip function → PR #64
The unzip function creates an array of ungrouped elements, where each resulting array contains all elements at a specific index from the input arrays. It's the functional opposite of zip.
import * as _ from 'radashi'
_.unzip([
['a', 1, true],
['b', 2, false],
])
// => [
// ['a', 'b'],
// [1, 2],
// [true, false],
// ]Allow filtering of object keys → PR #28
Improve your own utility function by adding a flexible key-filtering option, using filterKey to retrieve a filtering function. The returned function expects an object and a key, and returns true if the key passes the filter.
The following types can be casted into a filtering function:
- Array: If the input is an array, it returns a function that checks if the key is included in the array.
- Function: If the input is a function, it returns the function as is.
- Nullish: If the input is nullish (null or undefined), it returns a function that always returns
true.
import * as _ from 'radashi'
const obj = { a: 1, b: 2, c: 3 }
_.filterKey(obj, 'a', ['a', 'b']) // true
_.filterKey(obj, 'b', ['a', 'b']) // true
_.filterKey(obj, 'c', ['a', 'b']) // false
_.filterKey(obj, 'a', key => key < 'b') // true
_.filterKey(obj, 'b', key => key < 'b') // false
_.filterKey(obj, 'c', key => key < 'b') // false
_.filterKey(obj, 'a', null) // true
_.filterKey(obj, 'b', null) // true
_.filterKey(obj, 'c', null) // trueCheck for a plain object → PR #16
The isPlainObject function can be used to determine if a value is a plain object, as opposed to an instance of a custom class or a special object like Date.
import * as _ from 'radashi'
_.isPlainObject({}) // => true
_.isPlainObject(Object.create(null)) // => true
_.isPlainObject([]) // => false
_.isPlainObject(null) // => false
_.isPlainObject(new Date()) // => falseCheck for a Result tuple → PR #172
New functions have been added to help work with "Result" tuples, which represent the success or failure of a tryit call.
isResultchecks if a value is a Result tuple.isResultOkchecks if a Result tuple represents a successful operation.isResultErrchecks if a Result tuple represents a failed operation.
import * as _ from 'radashi'
if (_.isResult(value)) {
value // => [error?: unknown, result?: unknown]
}
if (_.isResultOk(value)) {
value[1] // <-- This is the resulting value!
}
if (_.isResultErr(value)) {
value[0] // <-- This is the error!
}🔗 isResult Docs / Source / Tests
🔗 isResultOk Docs / Source / Tests
🔗 isResultErr Docs / Source / Tests
Check for an integer string → commit fa500d3
The isIntString function returns true if the input is a string that represents an integer.
import * as _ from 'radashi'
_.isIntString('12') // => true
_.isIntString('-12') // => true
_.isIntString('12.233') // => false
_.isIntString('12.0') // => false
_.isIntString('+12') // => false
_.isIntString('hello') // => false
_.isIntString(null) // => false
_.isIntString(12) // => falseCheck for an error → PR #173
The isError function returns true for objects that inherit from Error.
import * as _ from 'radashi'
_.isError(new Error()) // => true
_.isError(new TypeError()) // => true
_.isError('An error occurred') // => false
_.isError({ message: 'Error' }) // => falseCheck for a boolean → commit adc419d
The isBoolean function returns true for boolean primitives. Boxed boolean values (e.g. new Boolean(false)) are not considered booleans by this function.
import * as _ from 'radashi'
_.isBoolean(true) // => true
_.isBoolean(false) // => true
_.isBoolean(new Boolean(true)) // => false
_.isBoolean('true') // => false
_.isBoolean(1) // => false
_.isBoolean(undefined) // => falseCheck for a RegExp object → PR #77
The isRegExp function returns true for RegExp instances, even if they are subclass instances or from other realms.
import * as _ from 'radashi'
_.isRegExp(/.+/) // true
_.isRegExp(new RegExp('.+')) // true
_.isRegExp(new (class extends RegExp {})('.+')) // trueCheck for a Set object → PR #77
The isSet function returns true for Set instances, even if they are subclass instances or from other realms.
import * as _ from 'radashi'
_.isSet(new Set()) // true
_.isSet(new (class extends Set {})()) // trueCheck for a Map object → PR #77
The isMap function returns true for Map instances, even if they are subclass instances or from other realms.
import * as _ from 'radashi'
_.isMap(new Map()) // true
_.isMap(new (class extends Map {})()) // trueCheck for a WeakMap object → PR #77
The isWeakMap and isWeakSet functions return true for WeakMap and WeakSet instances, respectively, even if they are subclass instances or from other realms.
import * as _ from 'radashi'
_.isWeakMap(new WeakMap()) // true
_.isWeakMap(new (class extends WeakMap {})()) // true
_.isWeakSet(new WeakSet()) // true
_.isWeakSet(new (class extends WeakSet {})()) // true🔗 isWeakMap Docs / Source / Tests
🔗 isWeakSet Docs / Source / Tests
Interpolate between two numbers → PR #86
The lerp function is used to linearly interpolate between two numbers based on a specified ratio between 0 and 1. For example, a ratio of 0.5 would return the number exactly halfway between the two input numbers.
import * as _ from 'radashi'
_.lerp(0, 10, 0.5) // => 5
_.lerp(5, 15, 0.2) // => 7
_.lerp(-10, 10, 0.75) // => 5noop and always → commit eb77c8f
The noop function is a callback that does nothing and returns undefined.
You don't call this directly. It's often useful in default parameters or destructuring to ensure a callback is defined.
import * as _ from 'radashi'
_.noop() // => undefined
_.noop(1) // => undefined
_.noop(1, 2, 3) // => undefinedThe always function creates a function that always returns the same value. This can be useful for providing a constant value as a callback. Note that the following example is overkill (for constant values, use an arrow function instead), but it demonstrates the concept:
import * as _ from 'radashi'
const alwaysTrue = _.always(true)
alwaysTrue() // true
alwaysTrue() // true
alwaysTrue() // true🔗 always Docs / Source / Tests
Flip the arguments of a function → PR #35
The flip function returns a new function that swaps the first two arguments of the original function.
This is most useful for reversing the order of a "comparator" (i.e. a function used for sorting), which effectively reverses the sort order.
import * as _ from 'radashi'
const subtract = (a: number, b: number) => a - b
subtract(1, 2) // => -1
_.flip(subtract)(1, 2) // => 1New Features
Allow debounce function to run immediately → PR #128
The debounce function now accepts a leading option. When true, the source function is called immediately on the first invocation of the debounced function, and subsequent calls will be debounced.
import * as _ from 'radashi'
const handleClick = _.debounce({ delay: 100, leading: true }, () => {
// This function will be called immediately on the first click,
// and then again after 100ms of no further clicks.
console.log('Clicked!')
})
handleClick() // Clicked!
handleClick() // (nothing)
handleClick() // (nothing)
// After 100ms: Clicked!Add trigger method to throttle function → PR #135
The throttle function now includes a trigger method on the returned throttled function. This allows the wrapped function to be invoked immediately, bypassing any throttling that may be in effect. After the trigger method is called, a new throttled call will be allowed after the specified interval has passed.
This can be useful when you want to ensure a function is called at least once, even if it's being throttled.
import * as _ from 'radashi'
const logMessage = (message: string) => {
console.log(`Message: ${message}`)
}
const throttledLog = _.throttle({ interval: 1000 }, logMessage)
throttledLog('First call') // Logs immediately
throttledLog('Throttled') // Doesn't log (throttled)
// Force a log, bypassing the throttle
throttledLog.trigger('Forced log') // Logs immediately
// Check if it's still throttled
throttledLog.isThrottled() // => trueAdd trailing option to throttle function → PR #127
The throttle function now accepts an optional trailing option, which controls whether the wrapped function should be called after the throttle period if it was invoked during the throttled time.
This can be useful when you want to ensure the function is called at the end of a series of rapid invocations, even if it's being throttled.
import * as _ from 'radashi'
// Throttle a scroll event handler, calling it after each throttle period
const handleScroll = () => {
console.log('Scroll position:', window.scrollY)
}
const throttledScroll = _.throttle(
{ interval: 200, trailing: true },
handleScroll,
)
window.addEventListener('scroll', throttledScroll)
// Throttle an API call, calling it after each throttle period
const throttledFetch = _.throttle(
{ interval: 5000, trailing: true },
async () => {
const response = await fetch('https://api.example.com/data')
const data = await response.json()
console.log(data)
},
)Provide index to mapify callbacks → PR #100
The mapify function now provides an index argument to the getKey and getValue callbacks. This allows you to base the key or value on the current index of the array item, in addition to the item itself.
import * as _ from 'radashi'
const items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' },
]
_.mapify(
items,
(item, index) => `item-${index}`,
item => item.name,
)
// Map {
// 'item-0' => 'Item 1',
// 'item-1' => 'Item 2',
// 'item-2' => 'Item 3',
// }Add reversible castComparator → commit 1d7937e
The castComparator function now accepts an optional reverse argument that, when true, will reverse the order of the comparison.
import * as _ from 'radashi'
const compareByName = _.castComparator('name')
const compareByNameReversed = _.castComparator('name', null, true)
const items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' },
]
items.sort(compareByName)
// => [
// { id: 1, name: 'Item 1' },
// { id: 2, name: 'Item 2' },
// { id: 3, name: 'Item 3' },
// ]
items.sort(compareByNameReversed)
// => [
// { id: 3, name: 'Item 3' },
// { id: 2, name: 'Item 2' },
// { id: 1, name: 'Item 1' },
// ]Allow pick function to accept a callback for advanced picking → PR #30
The pick function can also accept a predicate function as the filter argument. This allows for more complex filtering logic beyond simple key inclusion or exclusion.
import * as _ from 'radashi'
const source = { a: 1, b: 2, c: 3, d: 4 }
_.pick(source, (value, key) => {
return value % 2 === 0 // Include only even values
})
// => { b: 2, d: 4 }Let select function work without a condition callback → PR #9
The select function now allows you to omit the condition callback parameter. When the condition parameter is not provided, any nullish values returned by your mapping function will be filtered out.
import * as _ from 'radashi'
const list = [{ a: 1 }, { b: 2 }, { a: 3 }, { a: null }, { a: undefined }]
const result = _.select(list, obj => obj.a)
// result = [1, 3]Allow keys of any type in the unique function → PR #10
The unique function now supports any value as the key for identifying unique items in the array, not just strings, numbers, or symbols. The toKey callback argument can return any kind of value.
import * as _ from 'radashi'
const list: any[] = [
null,
null,
true,
true,
'true',
false,
{ id: 'a', word: 'hello' },
{ id: 'a', word: 'hello' },
]
const result = _.unique(list, val => (val && val.id) ?? val)
// result = [null, true, 'true', false, { id: 'a', word: 'hello' }]Allow keys of any type in the intersects function → PR #11
The intersects function now allows the identity callback argument to return any value, instead of just a string, number, or symbol.
As a result, you can now omit the identity callback for arrays of objects.
import * as _ from 'radashi'
const obj1 = { id: 1 }
const obj2 = { id: 2 }
const obj3 = { id: 3 }
const hasIntersection = _.intersects([obj1, obj2], [obj2, obj3])
// => true
const hasNoIntersection = _.intersects([obj1], [obj3])
// => falseBug Fixes
Fix camel-cased to pascal-cased string conversion → PR #178
The pascal function now correctly handles camel-cased strings, fixing issues with inputs like "helloWorld". It uses a regex to identify word boundaries and preserve existing capitalization.
import * as _ from 'radashi'
_.pascal('helloWorld') // => 'HelloWorld'Use -1 as index for toKey() with toggled item → PR #167
The toggle function has been updated to pass an index of -1 when calling the toKey function with the item to be toggled. This ensures that the toKey function can correctly identify the item, even if it doesn't exist in the array.
Previously, the toKey function was called with the current index of the item in the array, which could lead to incorrect behavior if the item didn't exist.
import * as _ from 'radashi'
const gods = ['ra', 'zeus', 'loki']
_.toggle(gods, 'vishnu', (item, index) => {
console.log(`Index for '${item}': ${index}`)
return item
})
// Console output:
// Index for 'vishnu': -1
// Index for 'ra': 0
// Index for 'zeus': 1
// Index for 'loki': 2
// => ['ra', 'zeus', 'loki', 'vishnu']Fix handling of period-containing property names → PR #95
The crush function has been updated to correctly handle object properties with periods in their names. Previously, such properties would always have an undefined value in the resulting object.
import * as _ from 'radashi'
const data = {
name: 'ra',
'children.0.name': 'hathor',
}
_.crush(data) // => { name: 'ra', 'children.0.name': 'hathor' }Fix null handling in assign → PR #112
The assign function has been updated to correctly handle the case where a nested object is being overridden with a null value. Previously, the function would incorrectly preserve the nested object, even when the override value was null.
import * as _ from 'radashi'
const initial = { name: 'ra', children: { age: 42 } }
const override = { children: null }
_.assign(initial, override) // => { name: 'ra', children: null }Handle falsy input as expected in toggle → PR #82
The toggle function now handles falsy input more consistently. Previously, a falsy item argument would be ignored, neither added nor removed from the array. Now, this only happens for undefined values. As such, you should still avoid having undefined in your array when using toggle with it (since it still can't be removed by toggle).
import * as _ from 'radashi'
_.toggle([], null) // [null]
_.toggle([null], null) // []
_.toggle([], false) // [false]
_.toggle([false], false) // []
_.toggle([], 0) // [0]
_.toggle([0], 0) // []
_.toggle([], '') // ['']
_.toggle([''], '') // []
_.toggle([], undefined) // []toInt and toFloat should not throw on symbols → PR #67
The toInt and toFloat functions now handle symbols more gracefully. Instead of throwing an error, they will return NaN if the input value is a symbol, and fall back to the provided default value if one is specified.
import * as _ from 'radashi'
_.toInt(Symbol('foo')) // NaN
_.toInt(Symbol('foo'), 0) // 0
_.toFloat(Symbol('bar')) // NaN
_.toFloat(Symbol('bar'), 0.0) // 0.0Use typeof in isFunction → commit 6ad96f4
The isFunction utility now uses the typeof operator instead of duck typing to determine if a value is a function. This ensures the type-guarded value can actually be called like a function, where before an object with call and apply properties would be falsely identified as a function.
import * as _ from 'radashi'
_.isFunction(() => {}) // => true
_.isFunction(MyClass) // => true
_.isFunction({ call: () => {}, apply: () => {} }) // => falseAvoid using isObject internally and use isPlainObject instead → PR #18
The internal use of isObject has been replaced with isPlainObject to ensure that objects created with Object.create(null) are handled correctly. This change improves the consistency and robustness of the library.
import * as _ from 'radashi'
const object = Object.create(null)
object.a = 1
object.b = [2]
object.c = { d: 3 }
_.assign({}, object) // { a: 1, b: [2], c: { d: 3 } }
_.keys(object) // ['a', 'b.0', 'c.d']Avoid false positive of array index in set function → PR #15
The set function has been improved to correctly handle keys that start with numbers. It now uses the isIntString function to detect if a key should be treated as an array index.
import * as _ from 'radashi'
_.set({}, 'cards.2value', 2) // { cards: { "2value": 2 } }Other Improvements
Performance
- assign
- Remove inefficiencies in loop. → PR #13
- cluster
- Avoid an array allocation by using a
forloop instead ofArray.fill().map()→ PR #63
- Avoid an array allocation by using a
- fork
- Avoid excessive array allocation by using a
forloop and direct array updates instead ofreduce()→ PR #33
- Avoid excessive array allocation by using a
- keys
- Avoid excessive array allocations. → PR #25
- merge
- Avoid calling the user-provided
keygenerator more than once per item and avoid the use of an arrow function in the loop. → PR #60
- Avoid calling the user-provided
- replace
- Avoid creating 2 intermediate arrays. → PR #61
- replaceOrAppend
- Avoid creating 2 intermediate arrays. → PR #62
- series
- Avoid object spread in the loop by using a
forloop and individual object property assignments instead → PR #37
- Avoid object spread in the loop by using a
- shuffle
- Use the Fisher-Yates algorithm. → PR #76
- template
- Use a
whileloop and string concatenation instead ofArray.from().reduce()→ PR #32
- Use a
Refactoring
- retry
- Stop using
range()unnecessarily, reducing the size ofretry. → commit 5d60893
- Stop using
Added Types
- Added
MemoOptions<T>type which represents the options object passed to thememofunction. → commit 877a1e4 - Added
TryitResult<T>type which represents the return type of thetryitfunction. → commit f044364 - Exported
UppercaseKeysandLowercaseKeystypes from thelowerizeandupperizefunctions respectively. → commit 96b28b9 - Add
FilteredKeystype that extracts the keys of an object that pass a given filter. → commit 6a6f899 - Add the Ok/Err/Result/ResultPromise types. → PR #132
- Add
Assigntype to represent the return type of theassignfunction. → PR #142
Type Fixes
- assign
- Make
assignreturn type more accurate. → PR #142
- Make
- group
- Ensure
group's return type is compatible withmapValues's function type. → PR #24
- Ensure
- isArray
- Make
isArraywork withreadonlyarray types. → commit 88c12b6 - Improve the
isArrayreturn type forunknowninput type. → PR #72 - Handle tuples in the
isArrayreturn type. → commit 9257535
- Make
- isPromise
- Align
isPromisereturn type with its logic. → PR #175
- Align
- omit
- Allow the
keysargument to be a readonly array. → PR #272
- Allow the
- select
- Make the
selectfunction more option-friendly by acceptingnullorundefinedfor theconditionargument. → commit c9cfcd0
- Make the
- series
- Allow the
itemsargument to be a readonly array → PR #14
- Allow the
- shake
- Stop using
Omiton return type and givefilterargument a safer type → PR #12
- Stop using
- shift
- Fix
shiftfunction to accept a readonly array type. → PR #126
- Fix
- sum
- Remove type constraint for mapped array passed to
sum. → PR #143
- Remove type constraint for mapped array passed to
- zip
- Allow
zipto accept readonly arrays. → commit f7d93cc
- Allow