Skip to content

nerdo/js-utils

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@nerdo/utils

nerdo's JavaScript utilities.

Install it:

npm install @nerdo/utils --save

isDateObject(object)

import { isDateObject } from '@nerdo/utils'

Tests whether an object is a Date object.

  • object {object}: the object in question.

isDateObject(...) returns true if the object is a Date object, false otherwise.

map(object, mapper)

import { map } from '@nerdo/utils'

A recursive mapping function for objects.

  • object {object}: the object to map.
  • mapper {function}: a function that handles the mapping.

map(...) returns the object

map traverses key-value pairs in an object recursively, calling the mapper on it and replacing each value with what the mapper returns.

mapper has the signature mapper(value, {object, path}).

  • value {mixed}: the current value being mapped.
  • object {object}: the root object the mapping is being performed on.
  • path: {array}: the list of keys identifying to the current value being mapped.

Here is simple example, filtering out all values that are numeric from the object:

const obj = {
    name: 'John Doe',
    age: 42,
    dateOfBirth: new Date(1973, 1, 7),
    misc: {
        height: 136,
        weight: 159
    }
}
const mapper = v => typeof v === 'number' ? undefined : v
map(obj, mapper)
console.log(obj) // {name: 'John Doe', dateOfBirth: 'Wed Feb 07 1973...', misc: {}}

Note, that the mapper will not traverse a value that is an object that has been removed by the mapper. Here is an example:

const obj = {
    foo: {
        removeMe: false,
        bar: {
            removeMe: true,
            ignored: {
                removeMe: true
            }
        }
    }
}
const mapper = function (v, {path}) {
    console.log(path.join('.'))
    return v.removeMe ? undefined : v
}
mapper(obj, mapper) // foo, foo.bar
console.log(obj) // {foo: {removeMe: false }}

Although both foo.bar and foo.bar.ignored have been removed by the mapper, the console logs confirm that the mapper never actually iterated on foo.bar.ignored. It was removed because it was a property of foo.bar, which was removed.

clone(object)

import { clone } from '@nerdo/utils'

Returns a deep clone of the specified object

  • object {object}: the object to clone.

clone(...) returns the deep cloned object

const obj = {
  a: [
    9,
    {
      foo: 'bar'
    },
    7
  ]
}
const cloned = clone(obj)
console.log(cloned) // a deep clone of obj

get(object, path, [defaultValue = undefined])

import { get } from '@nerdo/utils'

Returns the value in an object at the location specified by path.

  • object {object}: the object to query.
  • path {string|array}: the dotted-key string path or array path of the property to get.
  • defaultValue {mixed} [undefined]: the default value to return if the property path cannot be fully resolved.

get(...) returns the value, or defaultValue if the property path could not be fully resolved.

The path may be specified using either dotted-key or array path syntax. If the path cannot be fully resolved, the defaultValue is returned. For example:

const object = {a: [{b: {c: 3}}]}
get(object, 'a[0].b.c') // 3
get(object, ['a', 0, 'b', 'c']) // 3
get(object, 'a.b.c', 555) // 5

representMonth({ date, startingDayOfWeek })

import { representMonth } from '@nerdo/utils'

Generates an object representation of a calendar date.

  • date {representMonth.DateAdapter} [new representMonth.DateAdapter()]: the date to be represented.
  • startingDayOfWeek {representMonth.DayOfWeek} [representMonth.DayOfWeek.Sunday]: the starting day of the week.

Properties:

  • DateAdapter {Date}: a constructor that is compatible with the JavaScript Date object.

representMonth(...) returns an object with the following structure:

  • date {representMonth.DateAdapter}: the representMonth.DateAdapter instance of the date being represented.
  • numberOfDays {number}: the number of days in the month
  • weeks {(number | null)[][]}: the list of days in each week (i.e. 1 through 31 depending on the month). Each week array will always represent each of the 7 days and the first index of the week corresponds to the startingDayOfWeek argument. If a value is null, that particular day does not exist in the month. This is visually equivalent to when there are blank spaces in a calendar in the first and last weeks of a month that overlap with the previous month.
  • prevMonthNumberOfDays {number}: the number of days in the previous month
  • prevMonthLastWeek {(number | null)[]}: the last week of the previous month that overlaps with the first week of the current month.
  • nextMonthNumberOfDays {number}: the number of days in the next month
  • nextMonthFirstWeek {(number | null)[]}: the first week of the next month that overlaps with the last week of the current month.

A typical usage of this is to render calendars. For example:

const monthNames = [
  'January',
  'February',
  'March',
  'April',
  'May',
  'June',
  'July',
  'August',
  'September',
  'October',
  'November',
  'December'
]
const year = 2020
const cellSize = 4
const calWidth = 7 * cellSize + 8
monthNames.forEach((monthName, index) => {
  const label = `${monthName} ${year}`
  console.log(`${' '.repeat((calWidth - label.length) / 2)}${label}`) // centers the label over the calendar
  const representation = representMonth({date: new Date(year, index)}) //?
  const renderData: (string | number | null)[][] = [['S', 'M', 'T', 'W', 'T', 'F', 'S']]
  renderData
    .concat(representation.weeks)
    .map((week) => week
      .map((day) => `${day || ''}`) // coerce each day to a string
      .map((stringDay) => stringDay.padStart(cellSize, ' '))
      .join(' ')
    )
    .forEach((weekString) => console.log(weekString))
})

The preceeding code results in console logging of each month of the 2020 calendar year.

Here's a slightly more advanced example which uses the data to console log calendar months with previous and next months days showing up in square brackets (e.g. [30])

const monthNames = [
  'January',
  'February',
  'March',
  'April',
  'May',
  'June',
  'July',
  'August',
  'September',
  'October',
  'November',
  'December'
]
const year = 2020
const cellSize = 6
const calWidth = 7 * cellSize + 8
monthNames.forEach((monthName, index) => {
  const label = `${monthName} ${year}`
  console.log(`${' '.repeat((calWidth - label.length) / 2)}${label}`) // centers the label over the calendar
  const representation = representMonth({date: new Date(year, index)}) //?
  const renderData: (string | number | null)[][] = [['S', 'M', 'T', 'W', 'T', 'F', 'S']]
  renderData
    .concat(representation.weeks)
    .map((week, weekIndex) => {
      // checking weekIndex as if it were 1-based because we added the header for the week day labels
      const maybeFirstWeek = weekIndex === 1 ? representation.prevMonthLastWeek : void 0
      const maybeLastWeek = weekIndex === representation.weeks.length ? representation.nextMonthFirstWeek : void 0
      const edgeWeek = maybeFirstWeek || maybeLastWeek
      return week
        .map((day, index) => { // coerce each day to a string
          if (edgeWeek && edgeWeek[index]) return `[${edgeWeek[index] || ''}]`
          return `${day || ''}`
        }) 
        .map((stringDay) => stringDay.padStart(cellSize, ' '))
        .join(' ')
    })
    .forEach((weekString) => console.log(weekString))
})

isLeapYear(year: number): boolean

import { isLeapYear } from '@nerdo/utils'

Whether or not the year is a leap year.

  • year {number} the year

numDaysInMonth(month: number, year: number): number

import { numDaysInMonth } from '@nerdo/utils'

Gets the number of days in a calendar month.

  • month {number} the month; 0 = January, 11 = December
  • year {number} the year

DayOfWeek

import { DayOfWeek } from '@nerdo/utils'

An object representing the days of the week for use in representMonth().

Properties:

  • Sunday
  • Monday
  • Tuesday
  • Wednesday
  • Thursday
  • Saturday