Skip to content

Commit 8f54e73

Browse files
authored
feat: add concat function (#388)
1 parent a83a72d commit 8f54e73

File tree

7 files changed

+116
-1
lines changed

7 files changed

+116
-1
lines changed

.github/next-minor.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ The `####` headline should be short and descriptive of the new functionality. In
44

55
## New Functions
66

7-
####
7+
#### Add `concat` function
8+
9+
https://github.com/radashi-org/radashi/pull/388
810

911
## New Features
1012

benchmarks/array/concat.bench.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as _ from 'radashi'
2+
3+
bench('concat with simple values', () => {
4+
_.concat('a', null, 'b', undefined, 'c')
5+
})
6+
7+
bench('concat with nested arrays', () => {
8+
_.concat(1, [2, 3], null, 4, [5, [6, 7]])
9+
})
10+
11+
bench('concat with mixed types', () => {
12+
_.concat('a', 1, [true, { x: 2 }], null)
13+
})

docs/array/concat.mdx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
title: concat
3+
description: Flattens and filters nullish values from arguments
4+
---
5+
6+
### Usage
7+
8+
The `concat` function combines multiple values and arrays into a single array, while:
9+
10+
- Flattening nested arrays one level deep
11+
- Filtering out `null` and `undefined` values
12+
- Preserving the type information of the remaining values
13+
14+
Other falsy values (e.g. `0`, `false`, `''`, `NaN`) are preserved.
15+
16+
```ts
17+
import { concat } from 'radashi'
18+
19+
const result = concat('a', null, ['b', undefined], 'c')
20+
// => ['a', 'b', 'c']
21+
```

src/array/concat.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
export type Concat<T extends readonly any[]> = T[number] extends infer TElement
2+
? (TElement extends readonly (infer TNestedElement)[]
3+
? Exclude<TNestedElement, undefined | null>
4+
: Exclude<TElement, undefined | null>)[]
5+
: unknown[]
6+
7+
/**
8+
* Flattens and filters nullish values from arguments, returning a new
9+
* array containing only the non-nullish elements. Nested arrays are
10+
* flattened one level deep.
11+
*
12+
* @see https://radashi.js.org/reference/array/concat
13+
* @example
14+
* ```ts
15+
* const result = _.concat('', ['a'], undefined, [null, 'b'])
16+
* // => ['', 'a', 'b']
17+
* ```
18+
* @example
19+
* ```ts
20+
* const result = _.concat(1, [2, [3]], null)
21+
* // => [1, 2, [3]] // Note: only flattens one level
22+
* ```
23+
*/
24+
export function concat<T extends readonly [any, any, ...any[]]>(
25+
...values: T
26+
): Concat<T> {
27+
const result: any[] = []
28+
const append = (value: unknown) => value != null && result.push(value)
29+
for (const value of values) {
30+
if (Array.isArray(value)) {
31+
value.forEach(append)
32+
} else {
33+
append(value)
34+
}
35+
}
36+
return result as Concat<T>
37+
}

src/mod.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export * from './array/cartesianProduct.ts'
44
export * from './array/castArray.ts'
55
export * from './array/castArrayIfExists.ts'
66
export * from './array/cluster.ts'
7+
export * from './array/concat.ts'
78
export * from './array/counting.ts'
89
export * from './array/diff.ts'
910
export * from './array/first.ts'

tests/array/concat.test-d.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as _ from 'radashi'
2+
3+
describe('concat', () => {
4+
test('preserve non-nullish types', () => {
5+
const result = _.concat([] as readonly (string | null)[], false, 0)
6+
expectTypeOf(result).toEqualTypeOf<(string | boolean | number)[]>()
7+
})
8+
9+
test('preserve const types', () => {
10+
const result = _.concat([1, 2, null] as const, 3 as const)
11+
expectTypeOf(result).toEqualTypeOf<(1 | 2 | 3)[]>()
12+
})
13+
})

tests/array/concat.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import * as _ from 'radashi'
2+
3+
describe('concat', () => {
4+
test('nullish values are filtered out', () => {
5+
const result = _.concat('a', null, ['b', undefined], 'c')
6+
expect(result).toEqual(['a', 'b', 'c'])
7+
})
8+
9+
test('deeply nested arrays', () => {
10+
const result = _.concat(1, [2, [3, 4]], null)
11+
expect(result).toEqual([1, 2, [3, 4]])
12+
})
13+
14+
test('empty inputs', () => {
15+
expect(_.concat(null, undefined)).toEqual([])
16+
expect(_.concat([], null)).toEqual([])
17+
})
18+
19+
test('mixed types', () => {
20+
const result = _.concat(1, 'two', [true, { a: 1 }], null)
21+
expect(result).toEqual([1, 'two', true, { a: 1 }])
22+
})
23+
24+
test('other falsy values are preserved', () => {
25+
const result = _.concat(false, ['', 0], [null, undefined])
26+
expect(result).toEqual([false, '', 0])
27+
})
28+
})

0 commit comments

Comments
 (0)