Predilection for Predicates
Tags: javascript, functional
Published: 2021-09-08 13:34:48

---
title: Predilection for Predicates
published: true
description: Filtering arrays of Javascript objects declaratively using predicate functions
tags: javascript,functional
//cover_image: https://direct_url_to_image.jpg
---

I recently wanted to query arrays of Javascript objects using a declarative syntax and happily took the opportunity to write some small functions.

## Predicates

### EQ (is equal to)

```
const EQ = x => y => x === y
```
For anyone unfamiliar with ES6 arrow functions:

`EQ` is a function that accepts one argument (`x`) that returns another function that accepts one argument (`y`) which returns the result of evaluating `x === y`.

Here's the equivalent standard `function` def: 
```
function EQ (x) {
  return function (y) {
    return x === y
  }
}
```

### IN (is included in)

```
const IN = (...xs) => x => xs.includes(x)
```
`IN` is a function that accepts one or more arguments collected into an array (`xs`) that returns another function that accepts one argument (`x`) which returns the result of evaluating `xs.includes(x)`.

## Logical Operators
```
const NOT = pred => x => !pred(x)
```

```
const AND = (...preds) => x => preds.reduce((acc, pred) => acc && pred(x), true)
```

```
const OR = (...preds) => x => preds.reduce((acc, pred) => acc || pred(x), false)
```

## Doing Stuff

### Filtering Scalar Arrays

```
const data = [ 1, 2, 1, 1, 3, 2, 2, 2 ]
```

#### Get All the `1`s
```
>> data.filter(EQ(1))
Array(3) [ 1, 1, 1 ]
```

#### Get All the `1`s and `2`s
```
>> data.filter(IN(1, 2))
Array(7) [ 1, 2, 1, 1, 2, 2, 2 ]
```
```
>> data.filter(OR(EQ(1), EQ(2)))
Array(7) [ 1, 2, 1, 1, 2, 2, 2 ]
```

### Filtering Arrays of Objects

The above `EQ` and `IN` predicate functions work great with scalar values (i.e. numbers, booleans, etc), but I needed something that works on objects:

```
const OBJ = spec => obj => Object.entries(spec).reduce((acc, [k, pred]) => acc && pred(obj[k]), true)
```

`OBJ` accepts an object-type `spec` argument that maps key names to predicates.
For example, a `spec` value of:
```
{ isAdmin: EQ(true), active: EQ(true) }
```
would match objects with `isAdmin = true` AND `active = true`. For performing logical ops other than `AND`, you can specify them separately and wrap them appropriately. For example, to do an `OR` query on these same property values:
```
OR( OBJ({ isAdmin: EQ(true) }), OBJ({ active: EQ(true) }) )
```
Better may be to create an `OBJ_OR` or something but...moving on

#### Get some legit-looking data from [JSONPlaceholder](https://jsonplaceholder.typicode.com/)

```
const Todos = await (await fetch("https://jsonplaceholder.typicode.com/todos")).json()
```

The returned array looks like:
```
[
  {
    "userId": 1,
    "id": 1,
    "title": "delectus aut autem",
    "completed": false
  },
  ...
]
```

#### Find all uncompleted Todos from Users `1` and `2`:
```
>> Todos.filter(OBJ({userId: IN(1, 2), completed: EQ(false)}))
Array(21) [ {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, … ]
```
:tada:


## Optimizations omitted in favor of simplicity

- Short-circuit `AND` and `OR` on first `false` or `true` respectively instead of iterating over the entire array of object entries.

- Support implicit `EQ` for non-predicate-function object spec values, e.g. `{ isAdmin: true }` would be interpreted as `{ isAdmin: EQ(true) }`.