Skip to content
shucksterPublic

About

Apportion objects / arrays into multiple buckets based on a predicate / pattern.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Repository files navigation

sift-r 📥

MIT license Downloads per week npm bundle size Version

A complement to match-iz for filtering objects, arrays, and Map/Set based on a match-iz pattern.

byPattern()

import { byPattern } from 'sift-r'

const filtered = [
  { user: 'barney', age: 36, active: false },
  { user: 'fred', age: 40, active: true },
  { user: 'pebbles', age: 1, active: false }
].filter(
  byPattern({
    age: 36
  })
)

// filtered ===
//   [
//     { user: 'barney', age: 36, active: false }
//   ]

import { pluck, isNumber } from 'match-iz'

const mapped = [
  { user: 'barney', age: 36, active: false },
  { user: 'fred', age: 40, active: true },
  { user: 'pebbles', age: 1, active: false },
  { user: '', age: 'not-a-number', active: true }
].map(
  byPattern({
    age: pluck(isNumber)
  })
)

// mapped === [36, 40, 1, undefined]

sift() an object

import { sift } from 'sift-r'

const isString = x => typeof x === 'string'
const isNumber = x => typeof x === 'number'

const [strValues, numValues, neither] = sift(
  {
    title: 'header',
    slug: 1,
    markdown: '# header',
    footer: undefined
  },
  [isString, isNumber]
)

// strValues ===
//   {
//     title: 'header',
//     markdown: '# header'
//   }

// numValues ===
//   {
//     slug: 1
//   }

// neither ===
//   {
//     footer: undefined
//   }

sift() an array

(+using match-iz pattern-helpers)

import { sift } from 'sift-r'
import { gte, allOf } from 'match-iz'

const users = [
  { user: 'barney', age: 36, active: false },
  { user: 'fred', age: 40, active: true },
  { user: 'pebbles', age: 1, active: true }
]

const isActive = { active: true }
const isGrownUp = { age: gte(18) }
const isActiveGrownUp = allOf(isActive, isGrownUp)

const [activeGrownUps, everyoneElse] = sift(users, isActiveGrownUp)

// activeGrownUps ===
//   [
//     { user: 'fred', age: 40, active: true }
//   ]

// everyoneElse ===
//   [
//     { user: 'barney', age: 36, active: false },
//     { user: 'pebbles', age: 1, active: true }
//   ]

sift() an array into multiple buckets

import { sift } from 'sift-r'
import { lt } from 'match-iz'

const [oneYearOlds, lessThan40, theRest] = sift(
  [
    { user: 'barney', age: 36, active: false },
    { user: 'fred', age: 40, active: true },
    { user: 'pebbles', age: 1, active: false }
  ],
  { age: 1, active: false },
  { age: lt(40) }
)

// oneYearOlds ===
//   [{ user: 'pebbles', age: 1, active: false }]

// lessThan40 ===
//   [{ user: 'barney', age: 36, active: false }]

// theRest ===
//   [{ user: 'fred', age: 40, active: true }]

siftr() — curried sift

siftr(...schema) returns a function (input) => sift(input, ...schema) so you can fix the patterns once and reuse them (pipelines, .map, multiple collections).

import { siftr } from 'sift-r'
import { pluck, isNumber, lt } from 'match-iz'

const users = [
  { user: 'barney', age: 36, active: false },
  { user: 'fred', age: 40, active: true },
  { user: 'pebbles', age: 1, active: false }
]

const splitByAge = siftr({ age: 1 }, { age: lt(40) })
const [oneYearOlds, lessThan40, theRest] = splitByAge(users)

// Same schema, other inputs:
splitByAge(new Set(users))
splitByAge(users) // again

// With match-iz pluck: match buckets hold plucked payloads
const inactiveAges = siftr({ active: false, age: pluck(isNumber) })
const [ages, rest] = inactiveAges(users)
// ages === [36, 1]
// rest === [{ user: 'fred', age: 40, active: true }]

sift() a Map or Set

Set example:

import { sift } from 'sift-r'
import { lt } from 'match-iz'

const [oneYearOlds, lessThan40, theRest] = sift(
  new Set([
    { user: 'barney', age: 36, active: false },
    { user: 'fred', age: 40, active: true },
    { user: 'pebbles', age: 1, active: false }
  ]),
  { age: 1, active: false },
  { age: lt(40) }
)

// oneYearOlds ===
//   new Set([
//     { user: 'pebbles', age: 1, active: false }
//   ])

// lessThan40 ===
//   new Set([
//     { user: 'barney', age: 36, active: false }
//   ])

// theRest ===
//   new Set([
//     { user: 'fred', age: 40, active: true }
//   ])

Map example:

import { sift } from 'sift-r'
import { lt } from 'match-iz'

const [oneYearOlds, lessThan40, theRest] = sift(
  new Map([
    [0, { user: 'barney', age: 36, active: false }],
    [1, { user: 'fred', age: 40, active: true }],
    [2, { user: 'pebbles', age: 1, active: false }]
  ]),
  { age: 1, active: false },
  { age: lt(40) }
)

// oneYearOlds ===
//   new Map([
//     [2, { user: 'pebbles', age: 1, active: false }]
//   ])

// lessThan40 ===
//   new Map([
//     [0, { user: 'barney', age: 36, active: false }]
//   ])

// theRest ===
//   new Map([
//     [1, { user: 'fred', age: 40, active: true }]
//   ])

sift() an iterable

import { sift } from 'sift-r'
import { lt } from 'match-iz'

function* flintstones() {
  yield { user: 'barney', age: 36, active: false }
  yield { user: 'fred', age: 40, active: true }
  yield { user: 'pebbles', age: 1, active: false }
}

const [oneYearOlds, lessThan40, theRest] = sift(
  flintstones(),
  { age: 1, active: false },
  { age: lt(40) }
)

// oneYearOlds ===
//   [{ user: 'pebbles', age: 1, active: false }]

// lessThan40 ===
//   [{ user: 'barney', age: 36, active: false }]

// theRest ===
//   [{ user: 'fred', age: 40, active: true }]

TypeScript

sift-r ships detailed types in index.d.ts (no separate @types package).

Feature Behaviour
Container arity N patterns → N match buckets + 1 remainder (Array / Set / Map / iterable / pojo)
Type guards Up to five (x) => x is U arms: match buckets are Extract, remainder is Exclude of all matched unions
pluck (match-iz) Match buckets use the plucked payload type (MatchPayload); remainder keeps full elements
siftr Same typing as sift, curried
byPattern map/filter helper; with pluck, map results are payload | undefined
import { sift, siftr, byPattern } from 'sift-r'
import { pluck, isNumber, isString } from 'match-iz'

const isStr = (x: unknown): x is string => typeof x === 'string'
const isNum = (x: unknown): x is number => typeof x === 'number'

// Multi type-guard partition
const [strings, numbers, rest] = sift(
  ['a', 1, true] as Array<string | number | boolean>,
  isStr,
  isNum
)
// strings: string[]; numbers: number[]; rest: boolean[]

// Pluck transform in a reusable sifter
type User = { user: string; age: number; active: boolean }
const inactiveAges = siftr({ active: false, age: pluck(isNumber) })
const [ages, others] = inactiveAges([] as User[])
// ages: number[]; others: User[]

// byPattern + pluck
;([] as User[]).map(byPattern({ age: pluck(isNumber) }))
// Array<number | undefined>

Requires match-iz ≥ 5.1 for public pluck type helpers used by these definitions.

Install / Use

$ pnpm i sift-r

Supports import/require for ESM/CJS ("type": "module" package with dual exports).

Browser/UMD version here (global name is siftr — not to be confused with the siftr function export):

<script src="https://unpkg.com/sift-r/dist/browser/sift-r.browser.js"></script>
<script>
  const { sift, siftr, byPattern } = siftr
</script>

Documentation

sift() has several call-signatures, all of which are documented on the Wiki. See also the TypeScript section above for typing behaviour.

Credits

sift-r was written by Conan Theobald.

I hope you found it useful! If so, I like coffee ☕️ :)

License

MIT licensed: See LICENSE

About

Apportion objects / arrays into multiple buckets based on a predicate / pattern.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages