radash

repository·master·Indexed 26 days ago

https://github.com/sodiray/radash

A modern, simple, and powerful functional utility library designed for TypeScript. Version 12.1.1 provides a suite of typed utilities for array manipulation, error handling, and data transformation, including functions such as max, sum, fork, sort, boil, objectify, get, try, and map.

Tokens
24.1K
Snippets
118
Records
191
Agent score
89%

What's inside radash

  1. Understand Radash design principles

    master

    Radash is designed around several core principles to ensure ease of use and reliability:

    • Readability: Functions are implemented simply and concisely, making the source code easy to audit and understand.
    • Semi-Functional: While Radash leverages functional programming patterns (such as pure and deterministic functions), it avoids complex functional concepts like monads, making it accessible to developers without a deep functional programming background.
    • Safety: The library is built with TypeScript to provide full type safety out of the box.
  2. Use guard to handle async errors

    master

    The guard function allows you to execute an asynchronous function and return undefined instead of throwing an error if the function fails. This is useful for setting default values when an operation errors out using the nullish coalescing operator (??).

    You can optionally provide a predicate function as a second argument to guard to only catch specific error types. If the error does not match the predicate, the error will continue to propagate.

    // Return a default value if the async function errors out
    const users = (await guard(fetchUsers)) ?? []
    
    // Guard only specific errors using a predicate
    const isInvalidUserError = (err: any) => err.code === 'INVALID_ID'
    const user = (await guard(fetchUser, isInvalidUserError)) ?? DEFAULT_USER
  3. Use the sleep function to delay execution

    master

    The sleep function allows you to asynchronously wait for a specified amount of time in milliseconds. This is useful for introducing delays in asynchronous workflows.

    import { sleep } from 'radash'
    
    await sleep(2000) // => waits 2 seconds
  4. Use Radash utilities

    master

    Radash provides a suite of modern, typed functional utilities. You can import the entire library as a namespace (e.g., _) to access functions like max, sum, fork, sort, boil, objectify, get, try, and map.

    import * as _ from 'radash'
    
    const gods = [{
      name: 'Ra',
      power: 'sun',
      rank: 100,
      culture: 'egypt'
    }, {
      name: 'Loki',
      power: 'tricks',
      rank: 72,
      culture: 'norse'
    }, {
      name: 'Zeus',
      power: 'lightning',
      rank: 96,
      culture: 'greek'
    }]
    
    _.max(gods, g => g.rank) // => ra
    _.sum(gods, g => g.rank) // => 268
    _.fork(gods, g => g.culture === 'norse') // => [[loki], [ra, zeus]]
    _.sort(gods, g => g.rank) // => [ra, zeus, loki]
    _.boil(gods, (a, b) => a.rank > b.rank ? a : b) // => ra
    
    _.objectify(
      gods, 
      g => g.name.toLowerCase(), 
      g => _.pick(g, ['power', 'rank', 'culture'])
    ) // => { ra, zeus, loki }
    
    const godName = _.get(gods, g => g[0].name)
    
    // Using _.try for error handling in async calls
    const [err, god] = await _.try(api.gods.findByName)(godName)
    
    // Using _.map with async functions
    const allGods = await _.map(gods, async ({ name }) => {
      return api.gods.findByName(name)
    })
  5. Use defer to run async functions with cleanup tasks

    master

    The defer function allows you to run an asynchronous function while registering cleanup tasks that will be executed once the main function completes. This pattern is useful for ensuring resources (like directories or database records) are cleaned up regardless of whether the main function succeeds or fails, acting similarly to a finally block.

    Usage

    Pass an async function to defer. This function receives a register (or cleanup) callback. Inside your main logic, call this callback to schedule a task for later execution.

    Error Handling

    By default, if a registered cleanup function throws an error, that error is ignored. To change this behavior, you can pass an options object to the register function with { rethrow: true } to ensure cleanup errors are rethrown.

    import { defer } from 'radash'
    
    // Basic cleanup example
    await defer(async (cleanup) => {
      const buildDir = await createBuildDir()
    
      cleanup(() => fs.unlink(buildDir))
    
      await build()
    })
    
    // Example with rethrow strategy for cleanup errors
    await defer(async (register) => {
      const org = await api.org.create()
      register(async () => api.org.delete(org.id), { rethrow: true })
    
      const user = await api.user.create()
      register(async () => api.users.delete(user.id), { rethrow: true })
    
      await executeTest(org, user)
    })
  6. Compose data transformations using chain()

    master

    You can use chain to compose multiple transformation steps into a single reusable function. This is particularly useful when mapping over arrays to perform complex object property extractions and formatting.

    import { chain } from 'radash'
    
    type Deity = { 
      name: string
      rank: number 
    }
    
    const gods: Deity[] = [
      { rank: 8, name: 'Ra' },
      { rank: 7, name: 'Zeus' },
      { rank: 9, name: 'Loki' }
    ]
    
    const getName = (god: Deity) => god.name
    const upperCase = (text: string) => text.toUpperCase() as Uppercase<string>
    
    const getUpperName = chain(
      getName, 
      upperCase
    )
    
    getUpperName(gods[0])       // => 'RA'
    gods.map(getUpperName)      // => ['RA', 'ZEUS', 'LOKI']
  7. Toggle complex objects using an identity function

    master

    When working with arrays of objects, provide an identity function (often referred to as toKey) to toggle so it can correctly identify which object to add or remove based on a specific property.

    import { toggle } from 'radash'
    
    const ra = { name: 'Ra' }
    const zeus = { name: 'Zeus' }
    const loki = { name: 'Loki' }
    const vishnu = { name: 'Vishnu' }
    
    const gods = [ra, zeus, loki]
    
    toggle(gods, ra, g => g.name)     // => [zeus, loki]
    toggle(gods, vishnu, g => g.name) // => [ra, zeus, loki, vishnu]
  8. Flatten a deep object using `keys`, `get`, and `objectify`

    master

    You can flatten a deep object into a single-level object where keys are the dot-notation paths. To achieve this, combine keys to extract the paths, get to retrieve the values at those paths, and objectify to reconstruct the object.

    import { keys, get, objectify } from 'radash'
    
    const ra = {
      name: 'ra',
      power: 100,
      friend: {
        name: 'loki',
        power: 80
      },
      enemies: [
        {
          name: 'hathor',
          power: 12
        }
      ]
    }
    
    objectify(
      keys(ra),
      key => key,
      key => get(ra, key)
    )
    // => {
    //   'name': 'ra',
    //   'power': 100,
    //   'friend.name': 'loki',
    //   'friend.power': 80,
    //   'enemies.0.name': 'hathor',
    //   'enemies.0.power': 12
    // }
  9. Remove unwanted values from an object with shake()

    master

    The shake function creates a new object by removing unwanted attributes.

    By default, it removes all attributes where the value is undefined. You can also provide a predicate function as a second argument to remove attributes based on a custom condition.