Skip to content

crshmk/utils

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

66 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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' }

Releases

No releases published

Packages

No packages published