quick-n-dirty-utils

1.0.4 • Public • Published

Quick & Dirty Utilities

Little useful nuggets for accelerated web development.

npm install --save quick-n-dirty-utils 

IMPORTANT: PLEASE READ THE MIGRATION NOTES FOR VERSION 1.0.0+

Table of Contents

  1. Functions
    1. Date / Time
    2. Numbers
    3. REST
    4. Login
    5. Constants
    6. Colors
    7. Sorting
    8. Others

Functions

// import dependency
import { util } from "quick-n-dirty-utils"

// call a function of it
const range = util.range(1, 10)

Date / Time

All date / time functions use luxon (Luxon, the de-factor standard library for date/time) to provide the functionality.

formatDate(date, format)

Converts a Date object, Unix timestamp or luxon.DateTime object into a string for quick display of dates without a time component. The default format is yyyy-MM-dd.

Example:

import React from "react"
import { util } from "quick-n-dirty-utils"

const reactComponent = props => (
    <div>
        {/* prints out a date like 2020-02-18 */}
        <div>{util.formatDate(new Date())}</div>
        
        {/* prints out a date like 18/02/20 */}
        <div>{util.formatDate(new Date(), "dd/MM/yy")}</div>
    </div>
)

formatDateTime(dateTime, format)

Converts a Date object, Unix timestamp or luxon.DateTime object into a string for quick display of dates with a time component. If you require a custom format, you can provide this. The default format is dd/MM/yy T (e.g. 31/12/22 16:43)

Example:

import React from "react"
import { util } from "quick-n-dirty-utils"

const reactComponent = props => (
    <div>
        {/* prints out a date like 18/02/20 09:35 */}
        <div>{util.formatDateTime(new Date())}</div>
    </div>
)

applyTimeZoneOffset(timestamp, serverOffsetMin)

Used to offset mismatching server/client time zones in regards to timestamps. If your server provides Unix timestamps, but is located in a different timezone, then simply printing out those timestamps (as luxon.DateTime/Date) will print the time in the clients (browser) time zone, not the server time zone. Example: Your server provides timestamps for events and the event occurred at midnight, but you are located 2 hours behind the server's time zone. If you use that timestamp and print out the date (e.g. util.formatDateTime(new Date(myTimestamp))), it will show you 10pm, rather than midnight. Depending on the application, it might be useful to retain the local time.

The function requires you to provide the server time zone as offset in minutes. The client's time zone can be determined by the browser. Example offsets:

  • UTC+10 -> 600
  • UTC-5 -> -300

Example:

import { util } from "quick-n-dirty-utils"

const serverTimestamp = 1580777394

// server timezone is +10:00, so the serverOffsetMin is 600 (10 hours * 60 minutes)
let offsetClientDate = new Date(util.applyTimeZoneOffset(serverTimestamp, 10 * 60))

// server timezone is -08:00, so the serverOffsetMin is -480 (8 hours * 60 minutes)
offsetClientDate = new Date(util.applyTimeZoneOffset(serverTimestamp, -8 * 60))

Numbers

range(start, stop, step)

Familiar to Python programmers, this will create a list of sequential numeric values, e.g. [1, 2, 3, 4, 5].

Example:

import { util } from "quick-n-dirty-utils"

let range = util.range(1, 5)  // will produce [1, 2, 3, 4, 5]
range = util.range(5, 1, -1) // will produce [5, 4, 3, 2, 1]
range = util.range(1, 2, 0.5) // will produce [1, 1.5, 2]

normalise(val, min, max)

This is basically a percentage function that determines the percentage of val in the range of [min, max]. This currently only works for min < max.

Example:

import { util } from "quick-n-dirty-utils"

let percentage = util.normalise(3, 0, 20)  // returns 0.15 or 15%
percentage = util.normalise(10, 0, 20)  // return 0.5 or 50%

sum(list)

This is just a short hand for summing up numeric values in an array.

Example:

import { util } from "quick-n-dirty-utils"

const list = [5, 3.5, 10]
const sum = util.sum(list)  // returns 18.5

mean(list)

This is just a short hand for summing up numeric values in an array and dividing by its length.

Example

import { util } from "quick-n-dirty-utils"
const list = [3, 3, 6]
const avg = util.mean(list)  // returns 4.0

REST

getJsonHeader()

Small helper to just provide the Content-Type and Accept header for a fetch REST request.

Example:

import { util } from "quick-n-dirty-utils"

fetch("http://myurl.com", {
    headers: util.getJsonHeader(),
}).then(response => {
    // do something with the response
})

getAuthHeader(localStorageKey)

Small helper to just provide the Authorization header for a fetch REST request, if the token is stored in the browser's localStorage. This requires you to first save the required Authorization header value in the localStorage. The default key is "auth_token", but this can be customised for all quick&dirty functions involving authentication.

Example:

import { util } from "quick-n-dirty-utils"

fetch("http://myurl.com", {
    // will use the localStorage.getItem("auth_token") as Authorization header value; provide a custom key if required
    headers: util.getAuthHeader(),
}).then(response => {
    // do something with the response
})

getAuthJsonHeader(localStorageKey)

Combines the capabilities of getJsonHeader() and getAuthHeader(localStorageKey). If no localStorageKey is provided, the default (auth_token) will be used. This function is the default for a REST interface where the server requires authentication.

Example:

import { util } from "quick-n-dirty-utils"

fetch("http://myurl.com", {
    // will use the localStorage.getItem("my_token") as Authorization header value; provide a custom key if required
    headers: util.getAuthJsonHeader("my_token"),
}).then(response => {
    // do something with the response
})

restHandler(response)

By default fetch doesn't reject its promise, if the server responds with a status code >= 400 (e.g. 404, 500). This little helper can be used to process the result of the initial fetch promise to parse and resolve the response body to JSON, if the request was successful. If the request caused a status code >= 400, the promise will be rejected with the status code and the response body as payload.

Example:

import { util } from "quick-n-dirty-utils"

fetch("http://myurl.com", {
    headers: util.getAuthJsonHeader(),
})
    .then(util.restHandler)  // here we provide the handler as lambda for the response
    .then(jsonPayload => {
        // do something with the parsed JSON
    })
    .catch(err => {
        console.log("Request returned status code", err.status, "with message:", err.message)
    })

React State Handlers

When a React component uses a state variable to store a list of items any create/update/delete operation will have to modify that state variable. To help integrate new/updated items or remove items, the following 2 functions are available:

import { util } from "quick-n-dirty-utils"
import React from "react"

class MyComp extends React.Component {
    constructor(props) {
        super(props)
        this.state = { myList: [] }
    }
    saveItem(item) {
        // doesn't matter if add or update
        this.setState(oldState => ({
            ...oldState,
            myList: util.integrateDbItem(oldState.myList, item),
        }))
    }
    deleteItem(itemId) {
        this.setState(oldState => ({
            ...oldState,
            myList: util.removeDbItem(oldState.myList, itemId),
        }))
    }
}

Both functions will use the MongoDB convention and use the JSON key _id to match items. You can override that by providing a 3rd parameter: util.integrateDbItem(oldState.myList, item, "uid") and util.removeDbItem(oldState.myList, itemId, "uid") if for example your JSON key holding the unique ID is called uid.

Login

The login functions (including getAuthJsonHeader() and getAuthHeader()) assume that you use the browser's localStorage (a permanent storage in the browser per URL) to store the full header value for the Authorization header and of course that the server is using the Authorization header (vs. for example a cookie). So, if your auth headers look like this: Authorization: Basic x9fah736mad, you would store Basic x9fah736mad in the localStorage.

The default localStorage key used is auth_token, but this can be overridden in every function using the storage.

setAuthToken(token, localStorageKey)

Simple access wrapper for the browser's localStorage to store a login token. To be used by getAuthJsonHeader() and getAuthHeader(). The localStorage key can be overridden. Default is auth_token.

Example:

import { util } from "quick-n-dirty-utils"

fetch("http://myurl.com/login", {
    method: "POST",
    headers: util.getJsonHeader(),
    body: JSON.stringify({ username: "user", password: "mys3cret" }),
})
    .then(util.restHandler)
    .then(jsonPayload => {
        // assuming the JSON response contains the access token in the JSON body under a `userToken` key
        util.setAuthToken(jsonPayload.userToken)
    })

logout(localStorageKey)

Counterpart to setAuthToken(token, localStorageKey). This one deletes the key from the browser's localStorage.

Example:

import { util } from "quick-n-dirty-utils"

fetch("http://myurl.com/logout", {
    method: "POST",
    headers: util.getAuthHeader(),
}).then(response => {
    if (response.status === 200) {
        util.logout()
    }
})

Constants

DATE_FORMAT

A simple string used as the default date format: YYYY-MM-DD

DATE_TIME_FORMAT

A simple string used as the default date/time format: DD/MM/YY hh:mm

actionTypeSuffixes

This are suffixes used by redux for events regarding REST requests. If you use any request-promise middleware for Redux, this will be useful. Using these helpers will avoid typos and thus improve your code quality.

Example:

import { util } from "quick-n-dirty-utils"

const reduxAction = () => ({
    type: "myAction",
    payload: fetch("http://myurl.com/api/data").then(util.restHandler),
})
const initialState = { 
    data: [],
    dataLoading: false, 
}
const MyReducer = (state = initialState, action) => {
    switch (action.type) {
        case `myAction${util.actionTypeSuffixes.fulfilled}`: {
            // the myAction_fulfilled gets emitted when the REST call is completed successful.
            return {
                ...state,
                data: action.payload,
                dataLoading: false,
            }
        }   
        case `myAction${util.actionTypeSuffixes.pending}`: {
            // the type myAction_pending gets emitted as soon as the request has been started.
            return {
                ...state,
                dataLoading: true, 
            }
        }
        case `myAction${util.actionTypeSuffixes.rejected}`: {
            // the type myAction_rejected gets emitted if the request has failed (status code >= 400)
            return {
                ...state,
                dataLoading: false,
            }
        }
        default:
            return state
    }
}

If you dispatch reduxAction(), it will emit 2 actions: one with suffix _pending as soon as fetch has called the URL. And then when the response of that request comes in either _rejected or _fulfilled.

Colors

Some smaller helpers for handling colour gradients.

import { util } from "quick-n-dirty-utils"

util.getTricolor(0.5)  // returns white
util.getTricolor(0.7)  // returns pale blue
util.getTricolor(0.9, util.redGreenTricolor)  // returns almost saturated green

getTriColor(percent, colors)

Returns an rgb(r, g, b) value of the given percent value (expressed as float between 0.0 and 1.0) on a tri-color spectrum (e.g. red -> yellow -> green or blue -> white -> red).

The colors are defaulted to blue (1.0) -> white (0.5) -> red (0.0) and are provided as array of RGB arrays:

[
    [248, 105, 107], // red
    [255, 255, 255], // white
    [90, 138, 198], // blue
]

redGreenTricolor

Static field providing a colors setting for the getTriColor(..) function for red -> yellow -> green.

hexToRgb(hexValue, alpha)

Converts a colour provided as hexadecimal string into an RGB string, like rgb(255, 255, 255) that can be used in CSS. It allows an optional alpha parameter (default null), which will create a string like rgba(255, 255, 255, 0.3) for an alpha = 0.3. The hexValue can be provided with or without the # prefix.

Sorting

This library introduces a way to sort lists. It has capabilties to sort in ascending or descending order and supports lists of JSON objects with string, numeric or boolean values to sort by.

The basic sorting definition is a JSON object containing key and direction (one of asc or desc):

{
    "key": "date",
    "direction": "desc", 
}

It has good support for React component states and provides the following functions:

initSorting(sortKey, defaultDirection = null)

This will simply return a sorting object, which can be used by React as an initial component state. If the defaultDirection is not provided, asc will be used.

updateSorting(oldState, sortKey, stateKey = null, defaultDirection = null)

Updates a React component state's sorting definition. If the current sortKey is already selected, the sort direction will be inversed.

Parameter

  • oldState - the React component's old state (from this.setState(oldState => ({ ... })))
  • sortKey - the key that has been selected by the user to sort by
  • stateKey - by default this will be "sorting", which defines the key in oldState that contains the current sorting definition (using key and direction properties)
  • defaultDirection - defaults to "asc", if not provided - if the sortKey is not already selected, this will determine the initial sort direction.

Usage

import { util } from "quick-n-dirty-utils"

...
// initial state
this.state = {
    // this would set the sorting to "name" in ascending order in the constructor of the component
    sorting: {
        key: "name",
        direction: "asc",
    }
}
...
// change sorting to key "date" in descending order
this.setState(oldState => util.updateSorting(oldState, "date", null, "desc"))

sort(sorting, stringKeys = [], booleanKeys = [])

Helper function that returns an (a, b) => ... lambda that can be used to sort a list given a sorting definition and some information about which keys are strings and which ones are booleans.

Parameter

  • sorting - the current sorting definition, a JSON object with key and direction properties.
  • stringKeys - a list of JSON keys of the list items to be sorted that are strings (using .localeCompare to sort)
  • booleanKeys - a list of JSON keys of the list items to be sorted as booleans (first true, then false in ascending order)

Usage

import { util } from "quick-n-dirty-utils"
...
// initial state, set in the constructor of the react component
this.state = {
    sorting: {
        key: "name",
        direction: "asc",
    },
}
...
const items = [
    { name: "Peter", level: 5, isAdmin: true, department: "IT" },
    { name: "Janine", level: 4, isAdmin: false, department: "IT" },
    { name: "Paul", level: 3, isAdmin: false, department: "HR" },
]
...
// in the render function of a react component:
...
<div>
    {items.sort(util.sort(this.state.sorting, ["name", "department"], ["isAdmin"])).map(item => (
        <div key={item.name}>{item.name} ({item.level}) - {item.department} {item.isAdmin ? "ADMIN" : ""}</div>
    ))}
</div>
...

Others

exportToJson(objectData, filename)

Quickly download any JSON object as a JSON file. This only works in the browser and will internally create a Blob from the provided objectData (which has to be a valid JSON object or Array of JSON objects). It then creates a temporary hyperlink, which will trigger the download of a file with the given filename (defaults to export.json). The hyperlink is cleaned up after the download is triggered.

downloadFile(stringContent, contentType, fileName)

Downloads any string as file of the given content type. This only works in the browser and will internally create a Blob from the provided stringContent and creates a temporary hyperlink, which will trigger the download of the file. The fileName and contentType should be compatible.

toggleItem(list, item)

This function is useful for React state updates of lists where you want to add/remove an item depending on whether the item is already in the list. Only primitive entries (strings, numbers) are supported. The function will return the updated list, with either the item added, if it didn't exist in it before or removed, if it did. Removal will also modify the original list (using the splice operation).

getQueryStringParams(query)

This function will extract a URL query part (the part after the ?) and extract a JSON object with all key/value pairs being present. Note that all values will be strings, even if they represent numbers (you have to parse them on your end if necessary).

Example:

import { util } from "quick-n-dirty-utils"

const queryString = "?id=abc&page=5"
const query = util.getQueryStringParams(queryString)  // returns { id: "abc", page: "5" }

reverseMapping(jsonObject, showWarning = true)

This functions creates a reverse mapping for a flat JSON object mapping from keys to simple values. The values can be boolean, numbers or strings.

Example:

import { util } from "quick-n-dirty-utils"

const MAPPINMG = {
    key: "value1",
    foo: "bar",
}

const reverse = util.reverseMapping(MAPPING)
console.log(reverse.value1)  // will show "key"
console.log(reverse.bar) // will show "foo"

keyLookup(jsonMapping, lookupValue, showWarning = true)

This function will first call reverseMapping on the input JSON object and then try to find the lookupValue in that reversed mapping. This is useful for mappings from technical identifiers to labels. This is useful to avoid refactoring issues, when we would use the key of a JSON object as string.

Example:

import { util } from "quick-n-dirty-utils"

const MAPPING = {
    INVEST: "Investment and Portfolio",
    INVOICE: "Invoices and Receipts",
}

const DEFAULT_VIEW = util.keyLookup(MAPPING, MAPPING.INVOICE) // this will be "INVOICE"

jsonGet(object, key)

This simple function has the purpose to access nested JSON objects without having to manually check for null at each step.

Example:

import { util } from "quick-n-dirty-utils"

const myObject = {
    foo: {
        bar: {
            test: 1,
        },
    },
}

util.jsonGet(myObject, "foo")  // will return { bar: { test: 1 } }
util.jsonGet(myObject, "foo.bar.test") // will return 1
util.jsonGet(myObject, "foo.test.bar")  // will return null

mapListToKeyObject(list, keyOrFunction)

This function allows to map a list of uniform items to a flat JSON object, where each object will be stored under a key which is determined from the object by accessing a string key or executing a function on that item.

The result will be a JSON object. Underneath each key, it will store either a single item extracted using the key or function passed or a list of item, if multiple items map to the same key.

Example:

import { util } from "quick-n-dirty-utils"

const myList = [
    { _id: "a", name: "foo", level: 1, order: 1 },
    { _id: "b", name: "bar", level: 2, order: 2 },
    { _id: "c", name: "test", level: 1, order: 3 },
]

// using a unique key
const result1 = util.mapListToKeyObject(myList, "_id")
/*
 * will return:
 * {
 *   "a": { _id: "a", name: "foo", level: 1, order: 1 },
 *   "b": { _id: "b", name: "bar", level: 2, order: 2 },
 *   "c": { _id: "c", name: "test", level: 1, order: 3 },
 * }
 */

// using a key that has duplicates
const result2 = util.mapListToKeyObject(myList, "level")
/*
 * will return:
 * {
 *   1: [
 *     { _id: "a", name: "foo", level: 1, order: 1 },
 *     { _id: "c", name: "test", level: 1, order: 3 },
 *   ],
 *   2: { _id: "b", name: "bar", level: 2, order: 2 },
 * }
 */

// using a lambda
const result3 = util.mapListToKeyObject(myList, item => item[level] + item[order])
/*
 * will return:
 * {
 *   2: { _id: "a", name: "foo", level: 1, order: 1 },
 *   4: [
 *     { _id: "b", name: "bar", level: 2, order: 2 },
 *     { _id: "c", name: "test", level: 1, order: 3 },
 *   ],   
 * }
 */

getLast(array, defaultValue = null)

Retrieves the last element of an array or the provided defaultValue, in case the provided array is not an array or empty.

import { util } from "quick-n-dirty-utils"

const list = [0, 1, 2, 3, 4]
console.log(getLast(list))  // will return 4

arrayMatch(values, matchAgainst, minMatch = 1)

Finds out if an array of values occurs in another array of values. This is useful for filtering lists, which have multiple values for an attribute and you want the user to be able to filter by some of the values.

import { util } from "quick-n-dirty-utils"

const items = [
    { name: "Hello", tags: ["a"] },
    { name: "World", tags: ["c"] },
    { name: "Hello World", tags: ["a", "b"] },
]
let selectedTags = ["a", "b"]

const active = items.filter(i => util.arrayMatch(i.tags, selectedTags))
/* 
 * will return:
 * [{ name: "Hello", tags: ["a"] }, { name: "Hello World", tags: ["a", "b"] }]
 */
const twoActive = items.filter(i => util.arrayMatch(i.tags, selectedTags, 2))
/* will return:
 * [{ name: "Hello World", tags: ["a", "b"] }]
 */

arraySearch(values, filterFunction)

Finds the first match in a list of values that pass the filterFunction or null if no item in the list matches the function.

import { util } from "quick-n-dirty-utils"

const items = [0, 1, 2]
const result1 = util.arraySearch(items, val => val === 2)  // returns 2
const result2 = util.arraySearch(items, val => val === 3)  // returns null
const result3 = util.arraySearch(items, val => val > 0)  // returns 1 (first match)

Readme

Keywords

Package Sidebar

Install

npm i quick-n-dirty-utils

Weekly Downloads

1

Version

1.0.4

License

Apache-2.0

Unpacked Size

66.5 kB

Total Files

5

Last publish

Collaborators

  • ilfrich