npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ramjam

v1.3.1

Published

Functional JavaScript utilities that extend Ramda

Downloads

14

Readme

Ramda extensions

npm i ramjam

adjustBy

String => {a} => [{a}] => [{a}]

identify a collection item and merge updates

const adjustById = adjustBy('id')

const items = [
  { id: 1, x: 1, y: 1 },
  { id: 2, x: 2, y: 2 }
]

const update = { id: 2, x: 42 }
adjustById(update, items)
// [
//  { id: 1, x: 1, y: 1 },
//  { id: 2, x: 42, y: 2 }
//]

allAbsent

[a] => Boolean

allAbsent([[], {}, ''])
// true

allAbsent([[], {one: 1}, ''])
// false
const isSignupButtonShowing = allAbsent([user, signupToken])

allPresent

[a] => Boolean

allPresent([['a'], { one: 1 }, 'a', 1])
// true

allPresent([[], { one: 1 }, 'a', 1])
// false
const isConfigShowing = allPresent([user, activeSettingsTab])

anyAbsent

[a] => Boolean

anyAbsent([[], { one: 1 }, 'x'])
// true

anyAbsent([[42], { one: 1 }, 'x'])
// false
const isSpinnerShowing = allAbsent([user, guitars, fetchError])

anyPresent

[a] => Boolean

anyPresent([['a'], {}, '']))
// true

anyPresent([[''], {}, '']))
// false
const isDashboardShowing = anyPresent([user, guitars])

appendOrRemove

(a, [a]) => [a]

const state = ['one', 'two', 'three']

appendOrRemove('two', state)
appendOrRemove('four', state)
// ['one', 'three', 'four']
const onClickCheckbox = value => {
  const newSelections = appendOrRemove(value, selections)
  setSelections(newSelections)
}

appendState

stateSetter => item => void

append item or array of items onto state array

must be curried

const usePedals = () => {
  const [pedals, setPedals] = useState(['fuzz'])
  const addPedals = appendState(setPedals)
  return { addPedals, pedals }
}
const { addPedals } = usePedals() 
addPedals('delay')
// pedals state becomes ['fuzz', 'delay']
addPedals(['wah', 'phase'])
// pedals state becomes ['fuzz', 'delay', 'wah', 'phase']

camelToSnake

String => String

camelToSnake('oneTwoThree')
// 'one_two_three'
const camelKeysToSnake = mapKeys(camelToSnake)

const user = {
  familyName: '',
  givenName: ''
}

const createUserQuery = camelKeysToSnake(user)
// {
//   family_name: '',
//   given_name: ''
// }

extend

Function => String => Object => Object

augment an object by applying a function to it

const sumValues = pipe(values, sum)

const extendSum = extend(sumValues, 'sum') 

extendSum({ one: 1, two: 2 })
// { one: 1, two: 2, sum: 3 }

first

any => any

for collections; returns an empty object when passed an empty array or anything other than an array

first( [{one: 1}] )
// {one: 1}
first(null)
// {}
first([])
// {}

flatPick

[[String]] => {a} => {a}

const axiosErrorPaths = [
  ['message'],
  ['config', 'url'],
  ['config', 'data'],
  ['response', 'status'],
  ['response', 'statusText'],
  ['response', 'data']
]

const makeErrorResponse = flatPick(axiosErrorPaths)

const onError = axiosError => {
  const response = makeErrorResponse(axiosError)
  return Promise.reject(response)
}

axios.interceptors.response.use(prop('data'), onError)
// {
//   message: 'Request failed with status code 502',
//   url: '/users',
//   data: 'config data',
//   status: 502,
//   statusText: 'Bad Gateway',
//   response_data: 'response data'
// }

getQueryParams

window => {k: String}

// https://nikhuber-guitars.com/dealers?type=orca%2059&country=thailand"

getQueryParams(window)
// {
//    type: 'orca 59',
//    country: 'thailand'
//  }
import { useEffect, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { getQueryParams } from 'utils'

const useQueryParams = () => {
  const { search } = useLocation()
  const [queryParams, setQueryParams] = useState({})

  useEffect(() => {
    const newQueryParams = getQueryParams(window)
    setQueryParams(newQueryParams)
  }, [search])

  return queryParams
}

export default useQueryParams

getRouteFragments

window => [String]

// 'https://www.mesaboogie.com/en-US/Amp/?model=triple-rectifier'

getRouteFragments(window)
// ['en-US', 'Amp']

getUniqValues

String => [{a}] => [a]

const guitars = [
  { id: 1, make: 'Gibson', pickup: 'PAF' },
  { id: 2, make: 'Gibson', pickup: '57 Classic' },
  { id: 3, make: 'Fender', pickup: 'Lace Sensor' }
]

const getBrandOptions = getUniqValues('make')

getBrandOptions(guitars)
// ['Gibson', 'Fender']

isAbsent

  • => Boolean
isAbsent({})
isAbsent([])
isAbsent('')
isAbsent(null)
isAbsent(undefined)
// true

isAbsent({ one: 1 })
isAbsent(['a'])
isAbsent('a')
isAbsent(false) // <-- emptiness; not truthiness (not intended for bools)
// false
const isSignupButtonShowing = isAbsent(user)

isPresent

  • => Boolean
isPresent({ one: 1 })
isPresent(['a'])
isPresent('a')
isPresent(false) // <-- emptiness; not truthiness (not intended for bools)
// true

isPresent({})
isPresent([])
isPresent('')
isPresent(null)
isPresent(undefined)
// false
const isDashboardLinkShowing = isPresent(user)

makeQueryParams

{k: String} => String

const members = {
  guitar: 'Leo Nocentelli',
  keyboard: 'Art Neville'
}

makeQueryParams(members)
// ?guitar=Leo%20Nocentelli&keyboard=Art%20Neville

mapKeys

const toUpper = x => x.toUpperCase()
const gear = {
  cables: {
    toAmp: 'Boss',
    patch: 'George L'
  }
}

const toUpperKeys = mapKeys(toUpper)

toUpperKeys(gear)
// {
//    CABLES: {
//      TOAMP: 'Boss',
//      PATCH: 'George L'
//    }
// }

mapReplace

{k: String} => String => String

replaceFragments = mapReplace({
  'P Funk': 'P-Funk',
  In: 'in'
})

const makeBillboard = pipe(
  split('-'),
  map(ucFirst),
  join(' '),
  replaceFragments
)

const slug = 'p-funk-live-in-new-york'
makeBillBoard(slug)
// 'P-Funk Live in New York'

mergeState

stateSetter => {a} => void

const useUser = () => {
  const [user, setUser] = useState({})
  const updateUser = mergeState(setUser)
  return { updateUser, user }
}
const { updateUser } = useUser() 

updateUser({ id: 1, age: 42 })
updateUser({ age: 43, points: 2 })
// user is now { id: 1, age: 43, points: 2 }

nl2br

String => [String | br tag]

const titles = `The Bends
Ok Computer
Kid A`

nl2br(titles)
//['The Bends', <br />, 'Ok Computer', <br />, 'Kid A']  

notEmpty

  • => Boolean
complement(isEmpty)

prependState

stateSetter => item => void

prepend item or array of items onto state array

must be curried

const useInts = () => {
  const [ints, setInts] = useState([1])
  const prependInts = prependState(setInts)
  return { ints, prependInts }
}
const { ints, prependInts } = useInts() 
prependInts(2)
// ints state becomes [2, 1]
prependInts([3, 4])
// ints state becomes [3, 4, 2, 1]

propEq

classic propEq before it was broken in 0.29

prune

[[String]] => {a} => {a}

use a shape to create a new object from a given object props not on the given object are set to null

const axiosErrorPaths = [
  ['message'],
  ['config', 'url'],
  ['config', 'data'],
  ['response', 'status'],
  ['response', 'statusText'],
  ['response', 'data']
]

const makeErrorResponse = prune(axiosErrorPaths)
makeErrorResponse(axiosError)
// {  
//    message: 'Request failed with status code 502',
//    config: {
//      url: '/users',
//      data: 'config data'
//    },
//    response: {
//      status: 502,
//      statusText: 'Bad Gateway',
//      data: 'response data'
//    }
//  }

pruneOr

{a} => [[String]] => {a} => {a}

prune with defaults

const defaults = { 
  one: { 
    one1: 'default' 
  } 
}

const desiredShape = [ 
  ['one', 'one1'], 
  ['two', 'two1'], 
  ['four']
]

const toPrune = { 
  two: { 
    two1: 'incoming', 
    two2: 'incoming' 
  }, 
  three: 'incoming' 
}

const pruneResponse = pruneOr(defaults, desiredShape)
pruneResponse(toPrune)
{ 
  one: { 
    one1: 'default'
  },
  { 
    two: { 
      two1: 'incoming'
    } 
  }, 
  {
    four: null 
  }
}

removeBy

String => {a} | primitive => [{a}] => [{a}]

Remove collection items by matching key/value or value

const colors = [
  { id: 1, color: 'red' },
  { id: 2, color: 'green' },
  { id: 3, color: 'blue' }, 
  { id: 4, color: 'blue' }
]
   
const removeById = removeBy('id')
const itemToRemove = { id: 2, color: 'green' }
// this matches { id: 2 }
removeById(itemToRemove, colors)
// [
//   { id: 1, color: 'red' }, 
//   { id: 3, color: 'blue' }, 
//   { id: 4, color: 'blue' }
// ]

const removeColor = removeBy('color')
// this matches { color: 'blue' }
removeColor('blue', colors)
// [
//   { id: 1, color: 'red' }, 
//   { id: 2, color: 'green' }
// ]

removeStateBy

stateSetter => String => {a} | primitive => void

Remove collection items in state by matching key/value or value

Just toss whatever object you have at the state to remove it

const fetchedGuitars = [
  { id: 1, make: 'Gibson' },
  { id: 2, make: 'Gibson' },
  { id: 3, make: 'Fender' },
  { id: 4, make: 'Fender' }
]

const useGuitars = () => {
  const [guitars, setGuitars] = useState(fetchedGuitars)
  
  const removeGuitarBy = removeStateBy(setGuitars)
  const removeGuitar = removeGuitarBy('id')
  const removeGuitarMake = removeGuitarBy('make')

  return { guitars, removeGuitar, removeGuitarMake }
}

const { removeGuitar, removeGuitarMake } = useGuitars()

// this matches { id: 2 }
removeGuitar({ id: 2, make: 'Gibson' })
// guitars state becomes 
// [
//  { id: 1, make: 'Gibson' },
//  { id: 3, make: 'Fender' },
//  { id: 4, make: 'Fender' }
// ]

// this matches { make: 'Fender' }
removeGuitarMake('Fender')
// guitars state then becomes 
// [{ id: 1, make: 'Gibson' }]

renameKeys

{a} => {a} => {a}

const guitar = {
  guitarId: 42,
  transduction: 'SH4',
  config: {
    transduction: true
  },
  make: 'Gibson'
}

const guitarKeyReplacements = {
  guitarId: 'id',
  transduction: 'pickups'
}

const renameGuitarKeys = renameKeys(guitarKeyReplacements)
renameGuitarKeys(guitar)
// {
//   id: 42,
//   pickups: 'SH4',
//   config: {
//     pickups: true
//   },
//   make: 'Gibson'
// }

snakeToCamel

String => String

snakeToCamel('one_two_three')
// oneTwoThree
const snakeKeysToCamel = mapKeys(snakeToCamel)

const queryResult = {
  user_id: 42,
  given_name: '',
  family_name: ''
}

snakeKeysToCamel(queryResult)
// {
//   userId: 42,
//   givenName: '',
//   familyName: ''
// }

updateState

( [...args, currentState] -> newState ) => stateSetter => [...args] => void

curry state setters with transform functions

pass:

  • a transform function that takes the current state as its last argument and returns a modified state. You do not pass the state.
  • the state setter
  • arguments to be passed to the transform function before the current state

You must curry the first two args to create a declarative function that updates the state as a side effect.

const appendState = updateState(append)
const updateStateById = updateState(adjustBy('id'))

const useUser = () => {
  const [users, setUsers] = useState([])

  const addUser = appendState(setUsers)
  const updateUser = updateStateById(setUsers)

  return { addUser, updateUser, user }
}

const { addUser, updateUser } = useUsers()

addUser({ id: 1, age: 20 })
addUser({ id: 2, age: 30 })
updateUser({ id: 2, age: 31 })

// users state becomes 
// [
//   { id: 1, age: 20 },
//   { id: 2, age: 31 }
// ] 
const mergeState = updateState(mergeDeepLeft)

const useGuitar = () => {
  const [guitar, setGuitar] = useState({})
  
  const updateGuitar = mergeState(setGuitar)

  return { guitar, updateGuitar }
}

const { updateGuitar } = useGuitar()

updateGuitar({ id: 1, status: 'new' })
updateGuitar({ status: 'used', make: 'Gibson' })

// guitar state becomes 
// { id: 1, status: 'used', make: 'Gibson' }