Skip to content

Releases: MZanggl/flooent

v2.5.0

23 Feb 01:27
Compare
Choose a tag to compare

Added

Added "split" to array's pointer API

given.array(['a', 'is', 'c']).point(1).split() // [['a'], ['c']]

v2.4.2

21 Feb 11:34
Compare
Choose a tag to compare

Added

Added "remove" to the array point API

Removes the current index and returns a new array.

given.array(['music', 'tech']).point(1).remove() // ['music']

Fixed

  • Fixed TypeScript type issues for array's "where", "whereNot", "whereIn", "whereNotIn" methods
  • Fixed TypeScript type issues for number's "times" method

v2.4.1

03 Nov 14:41
Compare
Choose a tag to compare

Added

map.toObject

Turns the map into an object. Same as map.toJSON, but toJSON was meant for implicit transformations via JSON.stringify().
Use toObject for explicit object transformations.

add index to map.mapKeys and map.mapValues

These two methods now support a third argument that pass the current index along.

array.keyBy

Keys the collection by the given key and returns a flooent map.
If multiple items have the same key, only the last one will appear in the new collection.

const items = [{ id: 1, name: 'music' }, { id: 2, name: 'movie' }, { id: 3, name: 'music' }]
given.array(items).keyBy('name') // result is:
/*
{
  music: { id: 3, name: 'music' },
  movie: { id: 2, name: 'movie' }
}
*/

toKeyedMap

Turns the given array into a flooent map with each element becoming a key in the map.

const genres = ['music', 'tech', 'games']
const map = given.array(genres).toKeyedMap(null) // result is:
/*
{
  music: null,
  tech: null,
  games: null
}
*/

Alternatively, pass in a callback to specify the default value for each item individually:

const genres = ['music', 'tech', 'games']
const map = given.array(genres).toKeyedMap(genre => genre.toUpperCase()) // result is:
/*
{
  music: 'MUSIC',
  tech: 'TECH',
  games: 'GAMES'
}
*/

array.move additions

array.move now allows to pass the values "first" and "last" instead of just indexes:

given.array(['c', 'a', 'b']).move('first', 'after', 'last') // ['a', 'b', 'c']
given.array(['b', 'c', 'a']).move('last', 'before', 'first') // ['a', 'b', 'c']

New Experimental functional API

import { pipe, afterLast, beforeLast, endWith, capitalize } from 'flooent/fp/string'

const path = 'App/Controllers/user.js'
pipe(path, afterLast('/'), beforeLast('.'), endWith('Controller'), capitalize) // UserController

Note: flooent/fp/string, flooent/fp/map, flooent/fp/number, and flooent/fp/array all return the same function pipe.

v2.3.2

10 Jan 05:39
Compare
Choose a tag to compare

Fixed

  • Fixed wrong sort behaviour when sorting by a string field.
  • Fixed types for map.mapKeys and map.mapValues.
  • Fixed vulnerabilities in dev dependencies

v2.3.1

15 Nov 12:56
Compare
Choose a tag to compare
  • valueOf() can now also be called on arrays and maps to return the native array/map
  • Array.at is now called Array.point. Array.at is deprecated and will be removed in a future major version

Optional Functional API

12 Mar 13:58
Compare
Choose a tag to compare

Additions

Functional API

If you only need to do a single thing, you can also import most functions individually. The result of these functions will not be turned into a flooent object.

import { afterLast } from 'flooent/string'
afterLast('www.example.com', '.') // 'com'

import { move } from 'flooent/array'
move(['music', 'tech', 'sports'], 0, 'after', 1) // ['tech', 'music', 'sports']

import { times } from 'flooent/number'
times(3, i => i) // [0, 1, 2]

import { rename } from 'flooent/map'
rename(new Map([['item_id', 1]]), 'item_id', 'itemId') // Map { itemId → 1 }

Changes

string.between and string.betweenLast deprecated

These can already be expressed clearer using string.after, string.afterLast, string.before, and string.beforeLast without the need of a "nested fluent API". string.between and string.betweenLast will be removed in a future major release.

Add Map.rename, Array.move, and more

16 Feb 12:00
Compare
Choose a tag to compare

Added

rename

Renames the given key with the new key if found, keeping the original insertion order.

given.map({ one: 1, to: 2, three: 3 })
  .rename('to', 'two')
  .keys() // ['one', 'two', 'three']

move

Moves an item in the array using the given source index to either "before" or "after" the given target.

given.array(['b', 'a', 'c']).move(0, 'after', 1) // ['a', 'b', 'c']
given.array(['b', 'a', 'c']).move(0, 'before', 2) // ['a', 'b', 'c']
given.array(['b', 'a', 'c']).move(1, 'before', 0) // ['a', 'b', 'c']

Pointer API additions

value

Returns the value for current pointer position.

given.array(['music', 'tech']).at(1).value() // ['music', 'tech']

step

Steps forward or backwards given the number of steps.

given.array(['music', 'tech']).at(1).step(-1).value() // ['music']

Fixes

  • The callback in Array.reject now accepts the index as the second argument

Add Array.reject

15 Nov 11:00
Compare
Choose a tag to compare

Added

reject

Return all items that don't pass the given truth test. Inverse of Array.filter.

given.array([{ id: 1, disabled: true }]).reject(item => item.disabled) // []

Version 2

09 Aug 05:36
Compare
Choose a tag to compare

Version 2 focuses on three aspects: bundle size, consistency, and even more expressiveness, all while keeping the original goals of flooent.

  • The import cost went from 84.5kb all the way down to less than 10kb (3kb gzipped).
  • The number of external dependencies used went down from 15 to zero.
  • Every method now features JSDoc documentation

With this, flooent is now also a super useful tool for data manipulation on the frontend, since you no longer have to worry about the file size.

given is no longer a function

Before

import { given } from 'flooent'

given('hello') // instance of Stringable
given([1, 2]) // instance of Arrayable
given(1) // instance of Numberable
given({ key: 'value' }) // instance of Mappable (not object)

After

import { given } from 'flooent'

given.string('hello') // instance of Stringable
given.array([1, 2]) // instance of Arrayable
given.number(1) // instance of Numberable
given.map({ key: 'value' }) // instance of Mappable (not object)

This removes the internal magic of choosing the correct object and is generally more explicit.

macros

You can define macros on the new given methods without passing the object type.

Before

given.macro(String, 'scream', function() {
  return this.toUpperCase()
})

After

given.string.macro('scream', function() {
  return this.toUpperCase()
})

Furthermore, the following has been added to the macro-section of the README

  • how to deal with TypeScript
  • useful examples

Removed heavy yet uncommon methods

The below methods have been removed since they are not too common but together add up a lot to the bundle size. You can add them back through macros. Copy-pastable examples for how to do so have been added to the README under macros.

Removed string.plural & string.singular

Not only was this big in size it also only supported English. I'd rather look more into Intl and see how this could be solved for any language.

Removed array.is & array.quacksLike

TypeScript already takes care of this pretty well. Also, instead of comparing each value, which is expensive, it is often better to create a type guard. This leaves it to be only really useful for tests, for which there are already plenty of libraries and flooent is not really meant as an assertion library.

If you only want to shallow-clone an array or map you can wrap it in another given: given.array(flooentArray).

Removed array.clone & map.clone

Deep-cloning is usually an escape hatch for doing something the language cannot express on its own. For example, the array pointer API takes care of many cases that involve destructuring or cloning arrays. I'd rather add expressive methods to flooent that avoid deep cloning altogether.

Array.append and Array.prepend are now immutable

For consistent behavior across the project, these two methods have now been made immutable. If you prefer mutability, check out the new method mutate below.

Array.forget -> Array.omit

The name for the method Array.forget was changed to Array.omit. forget suggests mutability, but since this has always been immutable, omit makes more sense.

Another difference is that forget before accepted both an array and a string. This has been changed to only accept an array now.

const people = [
  { id: 1, age: 24, initials: 'mz' },
  { id: 2, age: 2, initials: 'lz' }
]

given.array(people).omit(['initials', 'age'])

pipe / when / whenEmpty only transforms the result into flooent variant when the result is of the respective type

Affected methods

  • array.pipe
  • string.pipe
  • array.when (using array.pipe under the hood)
  • string.when (using string.pipe under the hood)
  • string.whenEmpty (using string.pipe under the hood)

Before, string.pipe always converted the result back into a flooent string. Now, it only converts it back into a flooent string if the result is actually a string. Otherwise, it returns the raw result.

The same is true for array.pipe + array types respectively.

New methods and object

Array.mutate

Since all flooent array methods are now immutable, this is an escape hatch for when you need it.

const numbers = given.array([1, 2, 3])
numbers.mutate(n => n.append(4))
numbers // [1, 2, 3, 4]

Pointer set

Sets the value at the current index and returns a new array.

given.array(['music', 'tehc']).at(1).set(item => 'tech') // ['music', 'tech']

given.any

A generic helper class for any kind of data types.

do

Executes and returns the result of a callback.

This is useful for grouping common logic together and avoiding temporary variables.

Before

const user = User.first() // varialbe "user" is only used here
const nameMatches = expect(user.name).toBe('test name')

After

const nameMatches = given.any(User.first()).do(user => {
  return expect(user.name).toBe('test name')
})

New Pointer API

23 Jul 13:08
Compare
Choose a tag to compare

Let's you point to a specific index inside the array to do further actions on it.

given(['music', 'video', 'tech']).at(1) // returns pointer pointing to 'video'
given(['music', 'video', 'tech']).at(-1) // returns pointer pointing to 'tech'
given(['music', 'video', 'tech']).at(item => item === 'music') // returns pointer pointing to 'music'

append

Appends given value to array in between the currently pointed item and its next item.

given(['music', 'tech']).at(0).append('video') // ['music', 'video', 'tech']

prepend

Prepends given value to array in between the currently pointed item and its previous item.

given(['music', 'tech']).at(1).prepend('video') // ['music', 'video', 'tech']