2.0.1 • Published 2 years ago

sam-core v2.0.1

Weekly downloads
3
License
MIT
Repository
-
Last release
2 years ago

Sam-Core v1.2

sam-core is a small set of javascript (or typescript) extension methods for the built-in array and string types. It also contains a runTests method which can be used in conjuction with jest to both execute tests and get a consistent description of the tests. As part of the pretty-printing of runTests, two additional methods are exposed: trace and getMethodBody which are used to compute the test output.

In version 1.1.0, sam-core adds two new functions, arr and str which allow you to apply the new array and string methods without modifying the underlying types.

Table of Contents

Installation

yarn add -D sam-core

or

npm install -D sam-core

Release Notes

Version 1.2.12

The first and last array methods were rewritten so that they return undefined instead of throwing a RangeError, by default, when the array is empty or when no items in the array match the given predicate. This is more in keeping with how most node / javascript / typescript code is written. The second, optional parameter to both of these methods is now a boolean parameter which, if set to true, will cause a RangeError to be thrown if no items in the array satisfy the given predicate.

These changes also apply to the Arr class returned from the arr function.

See below for details.

Version 1.2.4

Added trace and getMethodBody methods.

Version 1.2.2

  • clear method was added to array and the arr wrapper method.
  • satisfies was added for the expected value of a test passed to runTests. This lets you test that something is true after the test method completes.

Version 1.2.x

In this version, the runTests method now allows the caller to specify that a test should throw an exception using the throws method.

describe('Array.first', () => runTests(
    [() => [].first(_, true), throws(RangeError)],  // should throw range error
    [() => [].first(), undefined]  // should return undefined
))

Also in this release, two new methods were added to array: first and last. These get the first or last item in an array, if no predicate is supplied, or the first or last item in an array that matches the supplied predicate. These were also added to the arr wrapper function.

Version 1.1.x

New to this version, sam-core now provides functions arr and str that give you access to the extension methods without modifying the built in class prototypes. To use them, import them from 'sam-core' and call the methods from the resulting object.

import { arr, str } from 'sam-core'

console.log(arr([0, 1, 2]).where(x => x > 0).valueOf())  // [1, 2]
console.log(str('JoeJoeBob').after('Joe').valueOf())  // 'JoeBob'

As long as you don't import 'sam-core' directly, the built in array and string types will not be modified.

These methods can be chained as in

console.log(str('JoeBobSue').after('Joe').before('Sue').valueOf())  // 'Bob'

Usage

To use the extension methods in your code, simply import the module.

import 'sam-core'

To use runTests, import it and use it conjuction with jest's describe method:

import 'sam-core'
import { runTests } from 'sam-core'

describe('add', () => runTests(
    [() => [1, 2, 3].add(5), [1, 2, 3, 5]],
    [() => [0].add('Joe'), [0, 'Joe']]
))

To use the arr or str functions, import them and wrap your native types by calling them.

import { arr, str } from 'sam-core'

console.log(str('JoeBobSue').after('Bob').valueOf())  // 'Sue'
console.log(arr([1, 2, 3]).first())  // 1

Array Extensions

add(item: T): T[]
all(predicate: Predicate<T>): boolean
any(predicate?: Predicate<T>): boolean
clear() : T[]
first(predicate?: Predicate<T>, throwIfNotFound?: boolean): T
last(predicate?: Predicate<T>, throwIfNotFound?: boolean): T
remove(item: T | ((item: T)=>boolean)): T[]
removeAll(predicate: Predicate<T>): T[]
removeAt(index: number, count?: number): T[]
select<U>(convert: (item: T) => U): U[]
selectMany<U>(select: (item: T) => U[]): U[]
where(predicate: Predicate<T>): T[]

where Predicate is defined as

type Predicate<T> = (item: T, index?: number) => boolean

You can import the Predicate type with

import { Predicate } from 'sam-core'

add(item: T): T[]

Adds an item to the end of an array (in-place) and returns the result.

alias: push()

Examples

import 'sam-core'

[1, 2, 4].add(6)  // [1, 2, 4, 6]
[].add('Joe')  // ['Joe']

all(predicate: Predicate\): boolean

Returns true if and only if the given predicate returns true for all items in the array.

alias: every()

Examples

import 'sam-core'

[1, 2, 3].all(x => x < 4)  // true
[1, 2, 3].all(x => x < 3)  // false

any(predicate?: Predicate\): boolean

Returns true if any only if the given predicate returns true for at least one item in the array. If no predicate is provided, then returns true if and only if the array contains one or more items.

alias: some()

Examples

import 'sam-core'

[].any()  // false
[1].any()  // true
[1, 2, 3].any(x => x < 3)  // true
[1, 2, 3].any(x => x > 3)  // false

clear(): T[]

New in version 1.2.2

Removes all items from the array (in place) and returns the empty array.

Examples

import 'sam-core'

[].clear()  // []
[1, 2, 3].clear()  // []

first(predicate?: Predicate\, throwIfNotFound?: boolean): T

Changed in version 1.2.12 New in version 1.2.0

Returns the first item in the array that matches the optional predicate, if the predicate is given. If no matches are found, then either returns undefined or throws a RangeError exception, if the optional throwIfNotFound parameter is set to true. If the predicate is not given, then returns the first item in the array. If the array is empty, then either returns undefined or throws a RangeError exception, if the optional throwIfNotFound parameter is set to true.

Examples

import 'sam-core'

[].first()  // undefined
[].first(_, true)  // throws RangeError
[1, 2, 3].first(x => x > 1)  // 2
[1, 2, 3].first(x => x > 3)  // undefined
[1, 2, 3].first(x => x > 3, true)  // throws RangeError

last(predicate?: Predicate\, defaultValue?: T): T

Changed in version 1.2.12 New in version 1.2.0

Returns the last item in the array that matches the optional predicate, if the predicate is given. If no matches are found, then either returns undefined or throws a RangeError exception, if the optional throwIfNotFound parameter is set to true. If the predicate is not given, then returns the last item in the array. If the array is empty, then either returns undefined or throws a RangeError exception, if the optional throwIfNotFound parameter is set to true.

Examples

import 'sam-core'

[].last()  // undefined
[].last(_, true)  // throws RangeError
[1, 2, 3].last(x => x > 1)  // 3
[1, 2, 3].last(x => x > 3)  // undefined
[1, 2, 3].last(x => x > 3, true)  // throws RangeError

remove(item: T): T[]

Removes the given item from the array (in-place) and returns the result. The item is compared using reference equality. If the item is not found, the array remains unchanged.

Examples

import 'sam-core'

[].remove('Joe')  // []
['Joe'].remove('Joe')  // []
[1, 2, 4].remove(2)  // [1, 4]
[1, 2, 4].remove(3)  // [1, 2, 4]

remove(predicate: ((item: T) => boolean)): T[]

Removes the first item from the array (in-place) that matches the given predicate and returns the result. If no items satisfy the predicate, the array remains unchanged.

Examples

import 'sam-core'

[].remove(item => 'Joe')  // []
['Joe'].remove(item => item == 'Joe')  // []
[1, 2, 4].remove(item => item > 1)  // [1, 4]
[1, 2, 4].remove(item => item == 3)  // [1, 2, 4]

removeAll(predicate: Predicate\): T[]

Removes all items from the array (in place) for which the given predicate holds true.

alias: filter()

Examples

import 'sam-core'

[].removeAll(item => true)  // []
[1, 2, 3].removeAll(Boolean)  // []
[1, 2, 3].removeAll(item => item > 1)  // [1]
[1, 2, 4].removeAll(item => item == 3)  // [1, 2, 4]

removeAt(index: number, count?: number): T[]

Removes the item from the array (in place) at the given index and returns the result. If the given index is less than 0 or greater than or equal to the number of items in the array, the array remains unchanged. If the optional count argument is not provided, only one item will be removed. If the count argument is less than 1, no items will be removed. If count is greater than 0, that many items will be removed or as many as remains, starting from index, will be removed.

alias: splice()

Examples

import 'sam-core'

[].removeAt(0)  // []
[1, 2, 3].removeAt(0)  // [2, 3]
[1, 2, 3].removeAt(-1)  // [1, 2, 3]
[1, 2, 3].removeAt(3)  // [1, 2, 3]
[1, 2, 3].removeAt(0, -1)  // [1, 2, 3]
[1, 2, 3].removeAt(0, 0)  // [1, 2, 3]
[1, 2, 3].removeAt(0, 1)  // [2, 3]
[1, 2, 3].removeAt(0, 2)  // [3]
[1, 2, 3].removeAt(0, 3)  // []
[1, 2, 3].removeAt(0, 4)  // []

select\(converter: (item: T) => U): U[]

Converts the array from type T to type U via a provided converter function and returns the result.

alias: map()

Examples

import 'sam-core'

[].select(x => x.toString())  // []
[1, 2, 3].select(x => s.toString())  // ["1", "2", "3"]
[1, 2, 3].select(x => x + 1)  // [2, 3, 4]
[0, 1, 2].select(Boolean)  // [false, true, true]

selectMany\(select: (item: T) => U[]): U[]

Merges zero or more nested arrays into a single array of items.

Examples

import 'sam-core'

[].selectMany(x => x)  // []
[[1, 2], [3, 4], [5, 6]].selectMany(x => x)  // [1, 2, 3, 4, 5, 6]
[
  { name: 'Joe', things: [1, 2, 3] },
  { name: 'Bob', things: [4, 5, 6] }
].selectMany(x => x.things)  // [1, 2, 3, 4, 5, 6]

where(predicate: Predicate\): T[]

Returns an array of the items for which the given predicate returns true.

alias: filter()

Examples

import 'sam-core'

[].where(x => x > 5)  // []
[1, 2, 3].where(_ => true)  // [1, 2, 3]
[1, 2, 3].where(_ => false)  // []
[1, 2, 3].where(x => x < 3)  // [1, 2]
[1, 2, 3].where(x => x > 3)  // [],
[1, 2, 3].where(x => x != 2)  // [1, 3]

String Extensions

after(mark: string | number): string
afterLast(mark: string): string
before(mark: string | number): string
beforeLast(mark: string | number): string
matches(expr: string | RegExp): boolean
toCamelCase(): string
toPascalCase(): string

after(index: number)

Returns the portion of the string that is after the given index. If the index is negative or greater than the length of the string, the original string is returned.

Examples

import 'sam-core'

'JoeBobAndSue'.after(3)  // 'BobAndSue'
'Joe'.after(4)  // 'Joe'
'Joe'.after(-1)  // 'Joe'

after(s: string)

Returns the portion of the string that is after the given substring. If the string is empty or not found in the string, the original string is returned.

Examples

import 'sam-core'

'JoeBobAndSue'.after('o')  // 'eBobAndSue'
'JoeBobAndSue'.after('Joe')  // 'BobAndSue'
'Joe'.after('e')  // ''
'Joe'.after('K')  // 'Joe'

afterLast(s: string)

Returns the portion of the string that is after the last occurrence of the given substring. If the string is empty or not found in the string, the original string is returned.

Examples

import 'sam-core'

'JoeBobAndSue'.afterLast('o')  // 'bAndSue'
'JoeBobAndSue'.afterLast('e')  // ''
'123123123'.afterLast('1')  // '23'
'Joe'.afterLast('K')  // 'Joe'

before(index: number)

Returns the portion of the string that is before the given index. If the index is negative or greater than the length of the string, the original string is returned.

Examples

import 'sam-core'

'Sam'.before(-1)  // 'Sam'
'Sam'.before(0)  // ''
'Sam'.before(1)  // 'S'
'Sam'.before(2)  // 'Sa'
'Sam'.before(3)  // 'Sam'

before(s: string)

Returns the portion of the string that is before the given substring. If the string is empty or not found in the string, the original string is returned.

Examples

import 'sam-core'

'JoeBobAndSue'.before('And')  // 'JoeBob'
'JoeBobAndSue'.before('Joe')  // ''
'Joe'.before('e')  // 'Jo'
'Joe'.before('K')  // 'Joe'

beforeLast(count: number)

Returns the left portion of the string that is the length of the string minus the given count. If the count is negative or zero, the original string is returned. If the count is greater than or equal to the length of the string, an empty string is returned.

Examples

import 'sam-core'

'JoeJoe'.beforeLast(-1) // 'JoeJoe'
'JoeJoe'.beforeLast(0) // 'JoeJoe'
'JoeJoe'.beforeLast(1) // 'JoeJo'
'JoeJoe'.beforeLast(2) // 'JoeJ'
'JoeJoe'.beforeLast(5) // 'J'
'JoeJoe'.beforeLast(6) // ''
'JoeJoe'.beforeLast(7) // ''

beforeLast(s: string)

Returns the portion of the string that is before the last occurrence of the given substring. If the string is empty or not found in the string, the original string is returned.

Examples

import 'sam-core'

'JoeBobAndSue'.beforeLast('o')  // 'JoeB'
'JoeBobAndSue'.beforeLast('e')  // 'JoeBobAndSu'
'123123123'.beforeLast('1')  // '123123'
'Joe'.beforeLast('K')  // 'Joe'

matches(expr: string | RegExp): boolean

Returns true if and only if the entire string is matched by the given expression. If the given expression does not start with the match beggining character (^) or does not end with the match end character ($), then these characters will be add to the expression.

Examples

import 'sam-core'

'abc.'.matches(/abc/)  // true
'abcd'.matches(/abc/)  // false
''.matches(/\s*/)  // true
'\t'.matches(/\s*/)  // true
'Joe'.matches('.*')  // true
'Joe'.matches('^.*')  // true
'Joe'.matches('.*$')  // true
'Joe'.matches('o') // false

toCamelCase(): string

Returns the given string with the first character in the string converted to lower case.

Examples

import 'sam-core'

''.toCamelCase()  // ''
'JoeJoe'.toCamelCase()  //  'joeJoe'
'joeJoe'.toCamelCase()  //  'joeJoe'

toPascalCase(): string

Returns the given string with the first character in the string converted to upper case.

Examples

import 'sam-core'

''.toPascalCase()  // ''
'JoeJoe'.toPascalCase()  //  'JoeJoe'
'joeJoe'.toPascalCase()  //  'JoeJoe'

runTests\(...tests: () => T, T)

Runs each test in the the given sequence by invoking jest's test method. Each item in the tests array is a pair: a method that can produce the actual result (the actual method), and the expected result. Each invocation of jest's test method is given a description based on the test being performed, and an invocation of jest's expect method followed by jest's toEqual method. If t is a test, then t[0] is the actual method and t[1] is the expected value and jest is invoked like this:

test(
    computeDescriptionOf(t[0], t[1]),
    expect(t[0]()).toEqual(t[1]))

Use runTests in conjuction with jest's describe method.

New in version 1.2.0

The test can expect an exception will be thrown via the throws method. See the Example below.

New in version 1.2.2

The test can require that something is true after the test is run using the satisfies method. See the Example below.

Example

import 'sam-core'
import { runTests, throws, satisfies } from 'sam-core'

describe('Array.removeAll, with instance = [1, 2, 3]', () => {
    const instance = [1, 2, 3]
    
    runTests(
        [() => [1, 2, 3].removeAll(_ => false), [1, 2, 3]],
        [() => [1, 2, 3].removeAll(x => x > 3), [1, 2, 3]],
        [() => [1, 2, 3].removeAll(x => x < 3), [3]],

        // the 'instance' array should have been modified in place
        [() => instance.removeAll(x => x >= 1), 
            satisfies(() => instance.length == 0)])  
})

describe('Array.first', () => runTests(
  // the test should throw a RangeError
  [() => [].first(), throws(RangeError)]  
))

The above would produce output like the following.

PASS src/array/tests/removeAll.test.ts
  Array.removeAll, with instance = [1, 2, 3]
    √ [1, 2, 3].removeAll(_ => false) == [1, 2, 3] (5ms)
    √ [1, 2, 3].removeAll(x => x > 3) == [1, 2, 3] (8ms)
    √ [1, 2, 3].removeAll(x => x < 3) == [3]
    √ instance.removeAll(_ => true) satisfies instance.length == 0 (1ms)

PASS src/array/tests/first.test.ts
  Array.first
    √ [].first() throws RangeError(...) (13ms)

The file tools\logs\tests.log at sam-core's git repository, has several examples of this type of output as most of sam-core was tested using runTests.

arr\(value: T[]): Arr\

New in version 1.1.0

Takes a given array and returns a wrapper class of type Arr<T> that has all of the array extension methods.

The Arr<T> wrapper class has the following methods on it:

toString(): string
valueOf(): T[]
add(item: T): Arr<T>
all(predicate: Predicate<T>): boolean
any(predicate?: Predicate<T>): boolean
clear(): Arr<T>
first(predicate?: Predicate<T>, throwIfNotFound?: boolean): T
last(predicate?: Predicate<T>, throwIfNotFound?: boolean): T
remove(item: T | ((value: T) => boolean)): Arr<T>
removeAll(predicate: Predicate<T>): Arr<T>
removeAt(index: number, count?: number): Arr<T>
select<U>(convert: (item: T) => U): Arr<U>
selectMany<U>(select: (item: T) => U[]): Arr<U>
where(predicate: Predicate<T>): Arr<T>

Call the valueOf method to get the underlying array.

str(value: T[]): Str

New in version 1.1.0

Takes a given string and returns a wrapper class of type Str that has all of the string extension methods.

The Str wrapper class has the following methods on it:

toString(): string
valueOf(): string
after(mark: string | number): Str 
afterLast(mark: string): Str
before(mark: string | number): Str
beforeLast(mark: string | number): Str
matches(expr: string | RegExp): boolean
toCamelCase(): Str
toPascalCase(): Str

Call the valueOf method to get the underlying string.

trace(x: any): string

New in version 1.2.4

Returns a human readable representation of the given argument.

Examples

import { trace } from 'sam-core'

const testDate = new Date('2010-11-26T04:26:33.000Z')

class Joe {
    constructor(
        public name: string = 'Joe',
        public age: number = 32,
        public alive: boolean = true) { }
}

trace(undefined)  // <undefined>
trace(null)  // <null>
trace(true)  // true
trace(false)  // false
trace(10)  // 10
trace(10.99)  // 10.99
trace("Joe")  // 'Joe'
trace(/aregexp/)  // /aregexp/
trace(['a', "Joe", 10, false])  // ['a', 'Joe', 10, false]
trace(x => x > 10)  // x => x > 10
trace(new Boolean(true))  // true
trace(new Number(20))  // 20
trace(new String('Joe'))  // 'Joe'
trace(new RegExp('Joe'))  // /Joe/
trace(testDate)  // new Date('2010-11-26T04:26:33.000Z')
trace({ name: "Joe", age: 32, alive: true })  // {name: 'Joe', age: 32, alive: true}
trace(new Joe())  // Joe {name: 'Joe', age: 32, alive: true}

getMethodBody\(method: (...args: any[]) => T): string

New in version 1.2.4

Returns the body portion of an inline method (not tested with function style functions).

Note that getMethodBody (and therefore runTests) is very opinionated about the formatting of the result and is intended for clean test result output. It may or not be in line with everyone's taste or sense of style.

Example

import 'sam-core'
import { trace, getMethodBody } from 'sam-core'

getMethodBody(() => trace('Joe'))  // trace('Joe')
getMethodBody(() => [1, 2, 3].add(5))  // [1, 2, 3].add(5)
2.0.1

2 years ago

2.0.0

2 years ago

1.2.13

4 years ago

1.2.12

4 years ago

1.2.8

4 years ago

1.2.7

4 years ago

1.2.9

4 years ago

1.2.10

4 years ago

1.2.11

4 years ago

1.2.6

4 years ago

1.2.5

4 years ago

1.2.4

4 years ago

1.2.0

4 years ago

1.2.3

4 years ago

1.2.2

4 years ago

1.2.1

4 years ago

1.1.1

4 years ago

1.1.0

4 years ago

1.0.15

4 years ago

1.0.14

4 years ago

1.0.9

4 years ago

1.0.8

4 years ago

1.0.11

4 years ago

1.0.10

4 years ago

1.0.13

4 years ago

1.0.12

4 years ago

1.0.7

4 years ago

1.0.6

4 years ago

1.0.5

4 years ago

1.0.4

4 years ago

1.0.3

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago