Skip to content

Commit

Permalink
feat: Add selectFirst
Browse files Browse the repository at this point in the history
  • Loading branch information
adamhamlin authored and aleclarson committed Jun 29, 2024
1 parent 4a26713 commit 8b9f9b2
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 4 deletions.
4 changes: 2 additions & 2 deletions .github/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ Scan through the [existing issues](https://github.com/aleclarson/radashi/issues)

## You want to write code?

- To get started, run `yarn` in the project's root directory to install the dependencies.
- You can run the unit tests with `yarn test`. They require Node v16+. You can run `nvm use` in the root directory to change to the correct Node version. The tests should pass (duh) and maintain 100% code coverage.
- To get started, run `pnpm i` in the project's root directory to install the dependencies.
- You can run the unit tests with `pnpm test`. They require Node v16+. You can run `nvm use` in the root directory to change to the correct Node version. The tests should pass (duh) and maintain 100% code coverage.
- To get familiar with the existing code I would recommend looking through the docs and the codebase in parallel. For each function in the docs, find the implementation in the source and skim over the code.
- When coding, try not to create internal APIs (any function or module that is not exported to be used by the client).
- Also, try not to use functions from other Radashi modules. This is a softer rule but we want to make it easy for readers to understand a function without having to navigate the codebase. As a utility library users should ideally be able to copy/paste a function from Radashi into their project. Most Radashi functions should be easy to write in isolation.
Expand Down
1 change: 1 addition & 0 deletions docs/array/select.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ description: Filter and map an array
## Basic usage

Applies a filter and a map operation at once and in one pass.
If the filter is omitted, returns all non-nullish mapped values.

```ts
import * as _ from 'radashi'
Expand Down
38 changes: 38 additions & 0 deletions docs/array/selectFirst.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
title: selectFirst
group: 'Array'
description: Array find + map
---

## Basic usage

Returns the mapped value for the first element that satisfies the specified condition--else undefined.
If the filter is omitted, returns the first non-nullish mapped value.

```ts
import * as _ from 'radashi'

const fish = [
{
name: 'Marlin',
weight: 105,
source: 'ocean',
},
{
name: 'Bass',
weight: 8,
source: 'lake',
},
{
name: 'Trout',
weight: 13,
source: 'lake',
},
]

_.selectFirst(
fish,
f => f.weight,
f => f.source === 'lake',
) // => 8
```
3 changes: 2 additions & 1 deletion src/array/select.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* Select performs a filter and a mapper inside of a reduce, only
* iterating the list one time.
* iterating the list one time. If condition is omitted, will
* select all mapped values that are non-nullish.
*
* ```ts
* select(
Expand Down
29 changes: 29 additions & 0 deletions src/array/selectFirst.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Select performs a find + map operation, short-circuiting on the first
* element that satisfies the prescribed condition. If condition is omitted,
* will select the first mapped value which is non-nullish.
*
* ```ts
* selectFirst(
* [1, 2, 3, 4],
* x => x * x,
* x => x > 2
* )
* // => 9
* ```
*/
export function selectFirst<T, U>(
array: readonly T[],
mapper: (item: T, index: number) => U,
condition?: (item: T, index: number) => boolean,
): U | undefined {
if (!array) {
return undefined
}
let foundIndex = -1
const found = array.find((item, index) => {
foundIndex = index
return condition ? condition(item, index) : mapper(item, index) != null
})
return found === undefined ? undefined : mapper(found, foundIndex)
}
2 changes: 1 addition & 1 deletion src/array/tests/select.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe('select function', () => {
)
expect(result).toEqual(['c2', 'd3'])
})
test('works without a condition callback', () => {
test('works without a condition callback, filtering nullish mapped values', () => {
const list = [{ a: 1 }, { b: 2 }, { a: 3 }, { a: null }, { a: undefined }]
const result = _.select(list, obj => obj.a)
expect(result).toEqual([1, 3])
Expand Down
60 changes: 60 additions & 0 deletions src/array/tests/selectFirst.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import * as _ from 'radashi'

const cast = <T = any[]>(value: any): T => value

describe('selectFirst function', () => {
test('does not fail on bad input', () => {
expect(
_.selectFirst(
cast(null),
x => x,
x => x,
),
).toBeUndefined()
expect(
_.selectFirst(
cast(undefined),
x => x,
x => x,
),
).toBeUndefined()
})
test('returns mapped result of first value that meets the condition', () => {
const list = [
{ group: 'a', word: 'hello' },
{ group: 'b', word: 'bye' },
{ group: 'a', word: 'oh' },
{ group: 'b', word: 'hey' },
{ group: 'c', word: 'ok' },
]
const result = _.selectFirst(
list,
x => x.word,
x => x.group === 'b',
)
expect(result).toEqual('bye')
})
test('does not fail on empty input list', () => {
const list: any[] = []
const result = _.selectFirst(
list,
(x: any) => x.word,
x => x.group === 'a',
)
expect(result).toBeUndefined()
})
test('works with index', () => {
const letters = ['a', 'b', 'c', 'd']
const result = _.selectFirst(
letters,
(l, idx) => `${l}${idx}`,
(_, idx) => idx > 1,
)
expect(result).toEqual('c2')
})
test('works without a condition callback, filtering nullish mapped values', () => {
const list = [{ a: null }, { a: undefined }, { b: 2 }, { a: 1 }, { a: 3 }]
const result = _.selectFirst(list, el => el.a)
expect(result).toEqual(1)
})
})
1 change: 1 addition & 0 deletions src/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export * from './array/range'
export * from './array/replace'
export * from './array/replaceOrAppend'
export * from './array/select'
export * from './array/selectFirst'
export * from './array/shift'
export * from './array/sift'
export * from './array/sort'
Expand Down

0 comments on commit 8b9f9b2

Please sign in to comment.